Просмотр исходного кода

Merge pull request #435 from emqtt/0.15

0.15 release - Support 500K+ subscribers to subscribe one topic
Feng Lee 10 лет назад
Родитель
Сommit
214fb752d4
100 измененных файлов с 725 добавлено и 2966 удалено
  1. 1 0
      .gitignore
  2. 3 0
      .gitmodules
  3. 46 0
      CHANGELOG.md
  4. 1 1
      LICENSE
  5. 1 3
      README.md
  6. BIN
      doc/MQTT_V3.1_Protocol_Specific.pdf
  7. BIN
      doc/design/Architecture.png
  8. 0 55
      doc/design/ClientSession.md
  9. 0 847
      doc/design/Design_Cluster.graphml
  10. BIN
      doc/design/Design_Cluster.png
  11. 0 274
      doc/design/Design_Standalone.graphml
  12. BIN
      doc/design/Design_Standalone.png
  13. BIN
      doc/design/PersistentSessionSeq1.png
  14. BIN
      doc/design/PersistentSessionSeq2.png
  15. BIN
      doc/design/PubSub_CleanSess_0.png
  16. BIN
      doc/design/PubSub_CleanSess_1.png
  17. 0 234
      doc/design/Seq.graphml
  18. 0 14
      doc/design/Seq.md
  19. BIN
      doc/design/Seq.png
  20. BIN
      doc/design/TransientSessionSeq1.png
  21. BIN
      doc/design/TransientSessionSeq2.png
  22. BIN
      doc/design/qos0_seq.png
  23. 0 993
      doc/emqttd.graphml
  24. BIN
      doc/emqttd.png
  25. BIN
      doc/mqtt-v3.1.1-os.pdf
  26. 0 7
      doc/pool.md
  27. BIN
      doc/rfc6455.pdf
  28. 0 19
      doc/uuid.md
  29. 0 0
      docs/.placeholder
  30. 2 2
      include/emqttd.hrl
  31. 1 1
      include/emqttd_cli.hrl
  32. 1 1
      include/emqttd_internal.hrl
  33. 1 1
      include/emqttd_protocol.hrl
  34. 1 0
      plugins/emqttd_plugin_redis
  35. 10 7
      rel/files/emqttd.config.development
  36. 10 4
      rel/files/emqttd.config.production
  37. 1 0
      rel/files/vm.args
  38. 0 1
      src/emqttd.app.src
  39. 20 5
      src/emqttd.erl
  40. 1 1
      src/emqttd_access_control.erl
  41. 1 1
      src/emqttd_access_rule.erl
  42. 1 1
      src/emqttd_acl_internal.erl
  43. 1 1
      src/emqttd_acl_mod.erl
  44. 1 1
      src/emqttd_alarm.erl
  45. 1 1
      src/emqttd_app.erl
  46. 1 1
      src/emqttd_auth_anonymous.erl
  47. 1 1
      src/emqttd_auth_clientid.erl
  48. 1 1
      src/emqttd_auth_ldap.erl
  49. 23 1
      src/emqttd_auth_mod.erl
  50. 14 5
      src/emqttd_auth_username.erl
  51. 2 2
      src/emqttd_bridge.erl
  52. 1 1
      src/emqttd_bridge_sup.erl
  53. 2 2
      src/emqttd_broker.erl
  54. 8 9
      src/emqttd_cli.erl
  55. 1 1
      src/emqttd_client.erl
  56. 1 1
      src/emqttd_cm.erl
  57. 1 1
      src/emqttd_cm_sup.erl
  58. 1 1
      src/emqttd_ctl.erl
  59. 1 1
      src/emqttd_dist.erl
  60. 1 1
      src/emqttd_gen_mod.erl
  61. 1 1
      src/emqttd_guid.erl
  62. 1 1
      src/emqttd_http.erl
  63. 1 1
      src/emqttd_keepalive.erl
  64. 1 1
      src/emqttd_log.erl
  65. 1 1
      src/emqttd_message.erl
  66. 2 2
      src/emqttd_metrics.erl
  67. 1 1
      src/emqttd_mnesia.erl
  68. 1 1
      src/emqttd_mod_presence.erl
  69. 1 1
      src/emqttd_mod_rewrite.erl
  70. 5 8
      src/emqttd_mod_subscription.erl
  71. 1 1
      src/emqttd_mod_sup.erl
  72. 133 67
      src/emqttd_mqueue.erl
  73. 1 1
      src/emqttd_net.erl
  74. 1 1
      src/emqttd_opts.erl
  75. 1 1
      src/emqttd_packet.erl
  76. 1 1
      src/emqttd_parser.erl
  77. 1 1
      src/emqttd_plugins.erl
  78. 1 1
      src/emqttd_pool_sup.erl
  79. 2 5
      src/emqttd_pooler.erl
  80. 1 1
      src/emqttd_protocol.erl
  81. 82 67
      src/emqttd_pubsub.erl
  82. 16 126
      src/emqttd_pubsub_helper.erl
  83. 49 14
      src/emqttd_pubsub_sup.erl
  84. 1 1
      src/emqttd_retainer.erl
  85. 229 129
      src/emqttd_router.erl
  86. 1 1
      src/emqttd_serializer.erl
  87. 1 1
      src/emqttd_session.erl
  88. 1 1
      src/emqttd_session_sup.erl
  89. 2 5
      src/emqttd_sm.erl
  90. 1 1
      src/emqttd_sm_helper.erl
  91. 1 1
      src/emqttd_sm_sup.erl
  92. 2 2
      src/emqttd_stats.erl
  93. 1 1
      src/emqttd_sup.erl
  94. 1 1
      src/emqttd_sysmon.erl
  95. 1 1
      src/emqttd_sysmon_sup.erl
  96. 11 9
      src/emqttd_topic.erl
  97. 1 1
      src/emqttd_trace.erl
  98. 1 1
      src/emqttd_trace_sup.erl
  99. 1 1
      src/emqttd_trie.erl
  100. 0 0
      src/emqttd_util.erl

+ 1 - 0
.gitignore

@@ -18,3 +18,4 @@ log/
 *.swp
 *.so
 examples
+docs/build/*

+ 3 - 0
.gitmodules

@@ -19,3 +19,6 @@
 [submodule "plugins/emqttd_recon"]
 	path = plugins/emqttd_recon
 	url = https://github.com/emqtt/emqttd_recon.git
+[submodule "plugins/emqttd_plugin_redis"]
+	path = plugins/emqttd_plugin_redis
+	url = https://github.com/emqtt/emqttd_plugin_redis.git

+ 46 - 0
CHANGELOG.md

@@ -2,6 +2,52 @@
 emqttd ChangeLog
 ==================
 
+0.15.0-beta(2016-01-28)
+-------------------------
+
+#### Highlights
+
+Optimization for Route ETS insertion (#427)
+
+Add Mongodb, Redis Plugins
+
+Priority Message Queue Support
+
+ReadTheDocs
+
+
+#### Enhancements
+
+Join/Leave the Cluster
+
+Username Authentication: Default Users
+
+Improve Cli commands: pubsub, bridges, trace
+
+emqttd_mod_subscription: fix client_connected/3
+
+
+#### BugFix
+
+Fix dequeue/1 of emqttd_bridge...
+
+Add emqttd:seed_now/0 function
+
+
+#### Plugins
+
+emqttd_plubin_mysql: changed mysql driver to mysql-otp
+
+emqttd_plugin_pgsql: integrate with ecpool
+
+emqttd_plugin_redis: first release
+
+emqttd_plugin_mongo: first release
+
+
+#### Benchmark
+
+
 0.14.1-beta(2015-12-28)
 -------------------------
 

+ 1 - 1
LICENSE

@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 1 - 3
README.md

@@ -5,11 +5,9 @@ emqttd is a massively scalable and clusterable MQTT V3.1/V3.1.1 broker written i
 
 emqttd requires Erlang R17+ to build.
 
-**DON'T compile the broker with Erlang/OTP R18.0 which introduced a [binary memory leak](http://erlang.org/pipermail/erlang-questions/2015-September/086098.html).**
-
 Demo Server: tcp://t.emqtt.io:1883
 
-Twitter: [@emqtt](https://twitter.com/emqtt)
+Follow us on Twitter: [@emqtt](https://twitter.com/emqtt)
 
 ## Goals
 

BIN
doc/MQTT_V3.1_Protocol_Specific.pdf


BIN
doc/design/Architecture.png


+ 0 - 55
doc/design/ClientSession.md

@@ -1,55 +0,0 @@
-## Transient Client/Session Sequence1
-
-```
-Client1->SM:  {start_session, {true, ClientId, self()}}
-SM-->Session: {destory, ClientId}
-Session-->Session: {shutdown, destroy}
-Session-->Client2: exit({shutdown, destroy})
-Client2-->CM: {'DOWN', MRef, process, Pid, Reason}
-SM-->Client1: {ok, SessPid}
-Client1-->CM: {register, Client1}
-```
-
-![Transient Client/Session Sequence1](TransientSessionSeq1.png)
-
-## Transient Client/Session Sequence2
-
-
-```
-Client1->SM:  {start_session, {true, ClientId, self()}}
-SM-->Session: {destory, ClientId}
-Session-->Session: {shutdown, destroy}
-SM-->Client1: {ok, SessPid}
-Client1-->CM: {register, Client1}
-Session-->Client2: exit({shutdown, destroy})
-Client2-->CM: {'DOWN', MRef, process, Pid, Reason}
-```
-
-![Transient Client/Session Sequence2](TransientSessionSeq2.png)
-
-## Persistent Client/Session Sequence1
-
-```
-Client1->SM: {start_session, {true, ClientId, self()}}
-SM-->Session: {resume, ClientId, ClientPid}
-Session-->Client2: {shutdown, conflict, {ClientId, Pid}}
-Client2-->CM: {unregister, ClientId, self()}
-SM-->Client1: {ok, SessPid}
-Client1-->CM: {register, Client1}
-```
-
-![Persistent Client/Session Sequence1](PersistentSessionSeq1.png)
-
-
-## Persistent Client/Session Sequence2
-
-```
-Client1->SM: {start_session, {true, ClientId, self()}}
-SM-->Session: {resume, ClientId, ClientPid}
-SM-->Client1: {ok, SessPid}
-Client1-->CM: {register, Client1}
-Session-->Client2: {shutdown, conflict, {ClientId, Pid}}
-Client2-->CM: {unregister, ClientId, self()}
-```
-
-![Persistent Client/Session Sequence2](PersistentSessionSeq2.png)

+ 0 - 847
doc/design/Design_Cluster.graphml

@@ -1,847 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
-  <!--Created by yEd 3.13-->
-  <key for="graphml" id="d0" yfiles.type="resources"/>
-  <key for="port" id="d1" yfiles.type="portgraphics"/>
-  <key for="port" id="d2" yfiles.type="portgeometry"/>
-  <key for="port" id="d3" yfiles.type="portuserdata"/>
-  <key attr.name="url" attr.type="string" for="node" id="d4"/>
-  <key attr.name="description" attr.type="string" for="node" id="d5"/>
-  <key for="node" id="d6" yfiles.type="nodegraphics"/>
-  <key attr.name="Description" attr.type="string" for="graph" id="d7"/>
-  <key attr.name="url" attr.type="string" for="edge" id="d8"/>
-  <key attr.name="description" attr.type="string" for="edge" id="d9"/>
-  <key for="edge" id="d10" yfiles.type="edgegraphics"/>
-  <graph edgedefault="directed" id="G">
-    <data key="d7"/>
-    <node id="n0" yfiles.foldertype="group">
-      <data key="d4"/>
-      <data key="d5"/>
-      <data key="d6">
-        <y:ProxyAutoBoundsNode>
-          <y:Realizers active="0">
-            <y:GroupNode>
-              <y:Geometry height="334.0" width="357.5" x="211.25" y="155.0"/>
-              <y:Fill color="#F5F5F5" transparent="false"/>
-              <y:BorderStyle color="#F5F5F5" type="dashed" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="node_width" borderDistance="0.0" configuration="CroppingLabel" fontFamily="Dialog" fontSize="18" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="27.19921875" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="357.5" x="0.0" y="153.400390625">emqttd cluster</y:NodeLabel>
-              <y:Shape type="roundrectangle"/>
-              <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
-              <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
-              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
-            </y:GroupNode>
-            <y:GroupNode>
-              <y:Geometry height="50.0" width="50.0" x="180.0" y="140.0"/>
-              <y:Fill color="#F5F5F5" transparent="false"/>
-              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
-              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
-              <y:Shape type="roundrectangle"/>
-              <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
-              <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
-              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
-            </y:GroupNode>
-          </y:Realizers>
-        </y:ProxyAutoBoundsNode>
-      </data>
-      <graph edgedefault="directed" id="n0:">
-        <node id="n0::n0">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="124.0" width="125.0" x="226.25" y="170.0"/>
-              <y:Fill color="#74C476" transparent="false"/>
-              <y:BorderStyle color="#74C476" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
-                <y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n1">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="309.25" y="223.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n2">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="268.25" y="223.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n3">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="249.25" y="254.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n4">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="284.25" y="254.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n5">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="284.25" y="193.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n6">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="124.0" width="125.0" x="428.75" y="170.0"/>
-              <y:Fill color="#74C476" transparent="false"/>
-              <y:BorderStyle color="#74C476" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
-                <y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n7">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="511.75" y="223.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n8">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="470.75" y="223.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n9">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="451.75" y="254.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n10">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="486.75" y="254.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n11">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="486.75" y="193.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n12">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="124.0" width="125.0" x="226.25" y="350.0"/>
-              <y:Fill color="#74C476" transparent="false"/>
-              <y:BorderStyle color="#74C476" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
-                <y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n13">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="309.25" y="403.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n14">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="268.25" y="403.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n15">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="249.25" y="434.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n16">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="284.25" y="434.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n17">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="284.25" y="373.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n18">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="124.0" width="125.0" x="428.75" y="350.0"/>
-              <y:Fill color="#74C476" transparent="false"/>
-              <y:BorderStyle color="#74C476" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
-                <y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n19">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="511.75" y="403.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n20">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="470.75" y="403.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n21">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="451.75" y="434.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n22">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="486.75" y="434.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n23">
-          <data key="d5"/>
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="486.75" y="373.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-      </graph>
-    </node>
-    <node id="n1">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="44.0" x="81.75" y="209.5"/>
-          <y:Fill color="#3182BD" transparent="false"/>
-          <y:BorderStyle color="#3182BD" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.6328125" x="16.68359375" y="13.43359375">P<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n2">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="44.0" x="81.75" y="389.5"/>
-          <y:Fill color="#756BB1" transparent="false"/>
-          <y:BorderStyle color="#756BB1" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.6328125" x="16.68359375" y="13.43359375">P<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n3">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="44.0" x="654.25" y="209.5"/>
-          <y:Fill color="#756BB1" transparent="false"/>
-          <y:BorderStyle color="#756BB1" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.462890625" x="16.7685546875" y="13.43359375">S<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n4">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="44.0" x="654.25" y="389.5"/>
-          <y:Fill color="#3182BD" transparent="false"/>
-          <y:BorderStyle color="#3182BD" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.462890625" x="16.7685546875" y="13.43359375">S<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <edge id="n0::e0" source="n0::n0" target="n0::n6">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#74C476" type="dashed" width="2.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e1" source="n0::n0" target="n0::n12">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#74C476" type="dashed" width="2.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e2" source="n0::n0" target="n0::n18">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#3182BD" type="dashed" width="1.0"/>
-          <y:Arrows source="none" target="standard"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e3" source="n0::n2" target="n0::n5">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e4" source="n0::n3" target="n0::n2">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e5" source="n0::n4" target="n0::n2">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e6" source="n0::n5" target="n0::n1">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e7" source="n0::n6" target="n0::n18">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#74C476" type="dashed" width="2.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e8" source="n0::n8" target="n0::n11">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e9" source="n0::n9" target="n0::n8">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e10" source="n0::n10" target="n0::n8">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e11" source="n0::n11" target="n0::n7">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e12" source="n0::n12" target="n0::n18">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#74C476" type="dashed" width="2.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e13" source="n0::n12" target="n0::n6">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#756BB1" type="dashed" width="1.0"/>
-          <y:Arrows source="none" target="standard"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e14" source="n0::n14" target="n0::n17">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e15" source="n0::n15" target="n0::n14">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e16" source="n0::n16" target="n0::n14">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e17" source="n0::n17" target="n0::n13">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e18" source="n0::n20" target="n0::n23">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e19" source="n0::n21" target="n0::n20">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e20" source="n0::n22" target="n0::n20">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e21" source="n0::n23" target="n0::n19">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e0" source="n0::n18" target="n4">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#3182BD" type="line" width="1.0"/>
-          <y:Arrows source="none" target="standard"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e1" source="n1" target="n0::n0">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#3182BD" type="line" width="1.0"/>
-          <y:Arrows source="none" target="standard"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e2" source="n2" target="n0::n12">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#756BB1" type="line" width="1.0"/>
-          <y:Arrows source="none" target="standard"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e3" source="n0::n6" target="n3">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#756BB1" type="line" width="1.0"/>
-          <y:Arrows source="none" target="standard"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-  </graph>
-  <data key="d0">
-    <y:Resources/>
-  </data>
-</graphml>

BIN
doc/design/Design_Cluster.png


+ 0 - 274
doc/design/Design_Standalone.graphml

@@ -1,274 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
-  <!--Created by yEd 3.13-->
-  <key for="graphml" id="d0" yfiles.type="resources"/>
-  <key for="port" id="d1" yfiles.type="portgraphics"/>
-  <key for="port" id="d2" yfiles.type="portgeometry"/>
-  <key for="port" id="d3" yfiles.type="portuserdata"/>
-  <key attr.name="url" attr.type="string" for="node" id="d4"/>
-  <key attr.name="description" attr.type="string" for="node" id="d5"/>
-  <key for="node" id="d6" yfiles.type="nodegraphics"/>
-  <key attr.name="Description" attr.type="string" for="graph" id="d7"/>
-  <key attr.name="url" attr.type="string" for="edge" id="d8"/>
-  <key attr.name="description" attr.type="string" for="edge" id="d9"/>
-  <key for="edge" id="d10" yfiles.type="edgegraphics"/>
-  <graph edgedefault="directed" id="G">
-    <data key="d7"/>
-    <node id="n0">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="44.0" x="130.0" y="200.0"/>
-          <y:Fill color="#3182BD" transparent="false"/>
-          <y:BorderStyle color="#3182BD" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.6328125" x="16.68359375" y="13.43359375">P<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n1">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="44.0" x="130.0" y="279.0"/>
-          <y:Fill color="#756BB1" transparent="false"/>
-          <y:BorderStyle color="#756BB1" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.6328125" x="16.68359375" y="13.43359375">P<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n2">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="44.0" x="458.0" y="202.5"/>
-          <y:Fill color="#756BB1" transparent="false"/>
-          <y:BorderStyle color="#756BB1" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.462890625" x="16.7685546875" y="13.43359375">S<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n3">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="44.0" x="458.0" y="277.5"/>
-          <y:Fill color="#3182BD" transparent="false"/>
-          <y:BorderStyle color="#3182BD" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.462890625" x="16.7685546875" y="13.43359375">S<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n4">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="124.0" width="125.0" x="250.0" y="193.0"/>
-          <y:Fill color="#74C476" transparent="false"/>
-          <y:BorderStyle color="#74C476" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
-            <y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n5">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="17.0" width="16.0" x="333.0" y="246.5"/>
-          <y:Fill color="#FD8D3C" transparent="false"/>
-          <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n6">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="17.0" width="16.0" x="292.0" y="246.5"/>
-          <y:Fill color="#FD8D3C" transparent="false"/>
-          <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n7">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="17.0" width="16.0" x="273.0" y="277.5"/>
-          <y:Fill color="#FD8D3C" transparent="false"/>
-          <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n8">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="17.0" width="16.0" x="308.0" y="277.5"/>
-          <y:Fill color="#FD8D3C" transparent="false"/>
-          <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n9">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="17.0" width="16.0" x="308.0" y="216.5"/>
-          <y:Fill color="#FD8D3C" transparent="false"/>
-          <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <edge id="e0" source="n0" target="n3">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
-            <y:Point x="270.0" y="222.5"/>
-            <y:Point x="360.0" y="300.0"/>
-          </y:Path>
-          <y:LineStyle color="#3182BD" type="dashed" width="1.0"/>
-          <y:Arrows source="none" target="plain"/>
-          <y:BendStyle smoothed="true"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e1" source="n1" target="n2">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
-            <y:Point x="270.0" y="300.0"/>
-            <y:Point x="360.0" y="225.0"/>
-          </y:Path>
-          <y:LineStyle color="#756BB1" type="dashed" width="1.0"/>
-          <y:Arrows source="none" target="plain"/>
-          <y:BendStyle smoothed="true"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e2" source="n6" target="n9">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e3" source="n7" target="n6">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e4" source="n8" target="n6">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e5" source="n9" target="n5">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-  </graph>
-  <data key="d0">
-    <y:Resources/>
-  </data>
-</graphml>

BIN
doc/design/Design_Standalone.png


BIN
doc/design/PersistentSessionSeq1.png


BIN
doc/design/PersistentSessionSeq2.png


BIN
doc/design/PubSub_CleanSess_0.png


BIN
doc/design/PubSub_CleanSess_1.png


Разница между файлами не показана из-за своего большого размера
+ 0 - 234
doc/design/Seq.graphml


+ 0 - 14
doc/design/Seq.md

@@ -1,14 +0,0 @@
-## QoS0 Publish Sequence 
-
-
-```
-title QoS0 Publish Sequence
-
-C1->PubSub: Publish QoS0
-PubSub-->S2: Dispatch QoS0
-S2-->C2: Deliver QoS0
-```
-
-## QoS1 Publish Sequence
-
-

BIN
doc/design/Seq.png


BIN
doc/design/TransientSessionSeq1.png


BIN
doc/design/TransientSessionSeq2.png


BIN
doc/design/qos0_seq.png


+ 0 - 993
doc/emqttd.graphml

@@ -1,993 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
-  <!--Created by yEd 3.13-->
-  <key for="graphml" id="d0" yfiles.type="resources"/>
-  <key for="port" id="d1" yfiles.type="portgraphics"/>
-  <key for="port" id="d2" yfiles.type="portgeometry"/>
-  <key for="port" id="d3" yfiles.type="portuserdata"/>
-  <key attr.name="url" attr.type="string" for="node" id="d4"/>
-  <key attr.name="description" attr.type="string" for="node" id="d5"/>
-  <key for="node" id="d6" yfiles.type="nodegraphics"/>
-  <key attr.name="Description" attr.type="string" for="graph" id="d7"/>
-  <key attr.name="url" attr.type="string" for="edge" id="d8"/>
-  <key attr.name="description" attr.type="string" for="edge" id="d9"/>
-  <key for="edge" id="d10" yfiles.type="edgegraphics"/>
-  <graph edgedefault="directed" id="G">
-    <data key="d7"/>
-    <node id="n0" yfiles.foldertype="group">
-      <data key="d4"/>
-      <data key="d6">
-        <y:ProxyAutoBoundsNode>
-          <y:Realizers active="0">
-            <y:GroupNode>
-              <y:Geometry height="334.0" width="357.5" x="242.0" y="185.0"/>
-              <y:Fill color="#F5F5F5" transparent="false"/>
-              <y:BorderStyle color="#F5F5F5" type="dashed" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="node_width" borderDistance="0.0" configuration="CroppingLabel" fontFamily="Dialog" fontSize="18" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="27.19921875" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="357.5" x="0.0" y="153.400390625">emqttd broker cluster</y:NodeLabel>
-              <y:Shape type="roundrectangle"/>
-              <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
-              <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
-              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
-            </y:GroupNode>
-            <y:GroupNode>
-              <y:Geometry height="50.0" width="50.0" x="180.0" y="140.0"/>
-              <y:Fill color="#F5F5F5" transparent="false"/>
-              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
-              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
-              <y:Shape type="roundrectangle"/>
-              <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
-              <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
-              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
-            </y:GroupNode>
-          </y:Realizers>
-        </y:ProxyAutoBoundsNode>
-      </data>
-      <graph edgedefault="directed" id="n0:">
-        <node id="n0::n0">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="124.0" width="125.0" x="257.0" y="200.0"/>
-              <y:Fill color="#74C476" transparent="false"/>
-              <y:BorderStyle color="#74C476" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
-                <y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n1">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="340.0" y="253.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n2">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="299.0" y="253.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n3">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="280.0" y="284.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n4">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="315.0" y="284.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n5">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="315.0" y="223.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n6">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="124.0" width="125.0" x="459.5" y="200.0"/>
-              <y:Fill color="#74C476" transparent="false"/>
-              <y:BorderStyle color="#74C476" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
-                <y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n7">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="542.5" y="253.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n8">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="501.5" y="253.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n9">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="482.5" y="284.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n10">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="517.5" y="284.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n11">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="517.5" y="223.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n12">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="124.0" width="125.0" x="257.0" y="380.0"/>
-              <y:Fill color="#74C476" transparent="false"/>
-              <y:BorderStyle color="#74C476" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
-                <y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n13">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="340.0" y="433.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n14">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="299.0" y="433.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n15">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="280.0" y="464.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n16">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="315.0" y="464.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n17">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="315.0" y="403.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n18">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="124.0" width="125.0" x="459.5" y="380.0"/>
-              <y:Fill color="#74C476" transparent="false"/>
-              <y:BorderStyle color="#74C476" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
-                <y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n19">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="542.5" y="433.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n20">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="501.5" y="433.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n21">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="482.5" y="464.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n22">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="517.5" y="464.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-        <node id="n0::n23">
-          <data key="d6">
-            <y:ShapeNode>
-              <y:Geometry height="17.0" width="16.0" x="517.5" y="403.5"/>
-              <y:Fill color="#FD8D3C" transparent="false"/>
-              <y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
-              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
-                  <y:SmartNodeLabelModel distance="4.0"/>
-                </y:LabelModel>
-                <y:ModelParameter>
-                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-                </y:ModelParameter>
-              </y:NodeLabel>
-              <y:Shape type="ellipse"/>
-            </y:ShapeNode>
-          </data>
-        </node>
-      </graph>
-    </node>
-    <node id="n1">
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="44.0" x="257.0" y="596.5"/>
-          <y:Fill color="#3182BD" transparent="false"/>
-          <y:BorderStyle color="#3182BD" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="37.861328125" x="3.0693359375" y="13.43359375">Client<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n2">
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="44.0" x="338.0" y="567.5"/>
-          <y:Fill color="#756BB1" transparent="false"/>
-          <y:BorderStyle color="#756BB1" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="42.994140625" x="0.5029296875" y="13.43359375">Sensor<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n3">
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="44.0" x="447.5" y="589.5"/>
-          <y:Fill color="#756BB1" transparent="false"/>
-          <y:BorderStyle color="#756BB1" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="42.994140625" x="0.5029296875" y="13.43359375">Sensor<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n4">
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="44.0" x="542.5" y="596.5"/>
-          <y:Fill color="#3182BD" transparent="false"/>
-          <y:BorderStyle color="#3182BD" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="37.861328125" x="3.0693359375" y="13.43359375">Client<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="ellipse"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n5">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="64.0" width="357.5" x="242.0" y="58.0"/>
-          <y:Fill color="#BDBDBD" transparent="false"/>
-          <y:BorderStyle hasColor="false" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="109.533203125" x="123.9833984375" y="22.933593750000014">Application Server<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n6">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="85.0" x="71.5" y="239.5"/>
-          <y:Fill color="#C0C0C0" transparent="false"/>
-          <y:BorderStyle color="#C0C0C0" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.50390625" x="28.248046875" y="13.43359375">Web<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n7">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="45.0" width="85.0" x="71.5" y="419.5"/>
-          <y:Fill color="#C0C0C0" transparent="false"/>
-          <y:BorderStyle color="#C0C0C0" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.50390625" x="28.248046875" y="13.43359375">Web<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n8">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="64.0" width="49.0" x="685.0" y="230.0"/>
-          <y:Fill color="#C0C0C0" transparent="false"/>
-          <y:BorderStyle color="#C0C0C0" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="43.052734375" x="2.9736328125" y="22.93359375">iPhone<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n9">
-      <data key="d5"/>
-      <data key="d6">
-        <y:ShapeNode>
-          <y:Geometry height="64.0" width="49.0" x="685.0" y="410.0"/>
-          <y:Fill color="#C0C0C0" transparent="false"/>
-          <y:BorderStyle color="#C0C0C0" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="50.58203125" x="-0.791015625" y="22.93359375">Android<y:LabelModel>
-              <y:SmartNodeLabelModel distance="4.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
-            </y:ModelParameter>
-          </y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <edge id="n0::e0" source="n0::n0" target="n0::n6">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#74C476" type="dashed" width="2.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e1" source="n0::n0" target="n0::n12">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#74C476" type="dashed" width="2.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e2" source="n0::n2" target="n0::n5">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e3" source="n0::n3" target="n0::n2">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e4" source="n0::n4" target="n0::n2">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e5" source="n0::n5" target="n0::n1">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e6" source="n0::n6" target="n0::n18">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#74C476" type="dashed" width="2.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e7" source="n0::n8" target="n0::n11">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e8" source="n0::n9" target="n0::n8">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e9" source="n0::n10" target="n0::n8">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e10" source="n0::n11" target="n0::n7">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e11" source="n0::n12" target="n0::n18">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#74C476" type="dashed" width="2.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e12" source="n0::n14" target="n0::n17">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e13" source="n0::n15" target="n0::n14">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e14" source="n0::n16" target="n0::n14">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e15" source="n0::n17" target="n0::n13">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e16" source="n0::n20" target="n0::n23">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e17" source="n0::n21" target="n0::n20">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e18" source="n0::n22" target="n0::n20">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n0::e19" source="n0::n23" target="n0::n19">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
-          <y:Arrows source="none" target="none"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e0" source="n0::n18" target="n4">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#3182BD" type="dashed" width="1.0"/>
-          <y:Arrows source="standard" target="standard"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e1" source="n2" target="n0::n12">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#756BB1" type="dashed" width="1.0"/>
-          <y:Arrows source="standard" target="standard"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e2" source="n0::n18" target="n3">
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="-22.749321892003195" sy="57.76129269460557" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#756BB1" type="dashed" width="1.0"/>
-          <y:Arrows source="standard" target="standard"/>
-          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="38.833984375" x="-31.872099564693826" y="37.92230224609375">MQTT<y:LabelModel>
-              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
-            </y:ModelParameter>
-            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
-          </y:EdgeLabel>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e3" source="n5" target="n0::n0">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="-101.25" sy="32.0078125" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#BDBDBD" type="line" width="1.0"/>
-          <y:Arrows source="none" target="standard"/>
-          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="-17.3134765625" y="29.92578125">HTTP<y:LabelModel>
-              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
-            </y:ModelParameter>
-            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
-          </y:EdgeLabel>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e4" source="n5" target="n0::n6">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="101.25" sy="31.9765625" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#BDBDBD" type="dashed" width="2.0"/>
-          <y:Arrows source="standard" target="standard"/>
-          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="38.833984375" x="-19.4169921875" y="29.92284393310547">MQTT<y:LabelModel>
-              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
-            </y:ModelParameter>
-            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
-          </y:EdgeLabel>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e5" source="n6" target="n0::n0">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#C0C0C0" type="dashed" width="1.0"/>
-          <y:Arrows source="standard" target="standard"/>
-          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="66.671875" x="16.89422607421875" y="-9.06640625">WebSocket<y:LabelModel>
-              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
-            </y:ModelParameter>
-            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
-          </y:EdgeLabel>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e6" source="n7" target="n0::n12">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#C0C0C0" type="dashed" width="1.0"/>
-          <y:Arrows source="standard" target="standard"/>
-          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="66.671875" x="16.89422607421875" y="-9.06640625">WebSocket<y:LabelModel>
-              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
-            </y:ModelParameter>
-            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
-          </y:EdgeLabel>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e7" source="n0::n6" target="n8">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#C0C0C0" type="dashed" width="1.0"/>
-          <y:Arrows source="standard" target="standard"/>
-          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="38.833984375" x="30.8330078125" y="-9.06640625">MQTT<y:LabelModel>
-              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
-            </y:ModelParameter>
-            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
-          </y:EdgeLabel>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e8" source="n0::n18" target="n9">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#C0C0C0" type="dashed" width="1.0"/>
-          <y:Arrows source="standard" target="standard"/>
-          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="38.833984375" x="30.8330078125" y="-9.06640625">MQTT<y:LabelModel>
-              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
-            </y:ModelParameter>
-            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
-          </y:EdgeLabel>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e9" source="n1" target="n0::n12">
-      <data key="d9"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#3182BD" type="dashed" width="1.0"/>
-          <y:Arrows source="standard" target="standard"/>
-          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="38.833984375" x="-8.698938402078966" y="-55.90826416015625">MQTT<y:LabelModel>
-              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
-            </y:LabelModel>
-            <y:ModelParameter>
-              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
-            </y:ModelParameter>
-            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
-          </y:EdgeLabel>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-  </graph>
-  <data key="d0">
-    <y:Resources/>
-  </data>
-</graphml>

BIN
doc/emqttd.png


BIN
doc/mqtt-v3.1.1-os.pdf


+ 0 - 7
doc/pool.md

@@ -1,7 +0,0 @@
-sup(one_for_all)
-    manager
-    pool_sup(one_for_one)
-        worker1
-        worker2
-        ...
-        workerN

BIN
doc/rfc6455.pdf


+ 0 - 19
doc/uuid.md

@@ -1,19 +0,0 @@
-## Mongodb ObjectId
-
-* 4-byte value representing the seconds since the Unix epoch,
-* 3-byte machine identifier,
-* 2-byte process id, and
-* 3-byte counter, starting with a random value.
-
-## Flake Id
-
-* 64bits Timestamp
-* 48bits WorkerId
-* 16bits Sequence
-
-## emqttd Id
-
-* 64bits Timestamp: erlang:now(), erlang:system_time
-* 48bits (node+pid): Node + Pid -> Integer 
-* 16bits Sequence: PktId
-

+ 0 - 0
docs/.placeholder


+ 2 - 2
include/emqttd.hrl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -28,7 +28,7 @@
 %%------------------------------------------------------------------------------
 %% Banner
 %%------------------------------------------------------------------------------
--define(COPYRIGHT, "Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>").
+-define(COPYRIGHT, "Copyright (C) 2012-2016, Feng Lee <feng@emqtt.io>").
 
 -define(LICENSE_MESSAGE, "Licensed under MIT"). 
 

+ 1 - 1
include/emqttd_cli.hrl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
include/emqttd_internal.hrl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
include/emqttd_protocol.hrl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
+%%% @Copyright (C) 2012-2016, Feng Lee <feng@emqtt.io>
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 0
plugins/emqttd_plugin_redis

@@ -0,0 +1 @@
+Subproject commit c6a532d49d2b479551bfd3b8d278d40c99e96ae3

+ 10 - 7
rel/files/emqttd.config.development

@@ -85,9 +85,6 @@
         {client, [
             %% Socket is connected, but no 'CONNECT' packet received
             {idle_timeout, 10} %% seconds
-            %TODO: Network ingoing limit
-            %{ingoing_rate_limit, '64KB/s'}
-            %TODO: Reconnet control
         ]},
         %% Session
         {session, [
@@ -111,11 +108,17 @@
             {expired_after, 48}
 
         ]},
-        %% Session
+        %% Queue
         {queue, [
-            %% Max queue length. enqueued messages when persistent client disconnected, 
+            %% simple | priority
+            {type, simple},
+
+            %% Topic Priority: 0~255, Default is 0
+            %% {priority, [{"topic/1", 10}, {"topic/2", 8}]},
+
+            %% Max queue length. Enqueued messages when persistent client disconnected,
             %% or inflight window is full.
-            {max_length, 100},
+            {max_length, infinity},
 
             %% Low-water mark of queued messages
             {low_watermark, 0.2},
@@ -147,7 +150,7 @@
         %% PubSub and Router
         {pubsub, [
             %% Default should be scheduler numbers
-            %% {pool_size, 8},
+            {pool_size, 8},
             
             %% Subscription: disc | ram | false
             {subscription, ram},

+ 10 - 4
rel/files/emqttd.config.production

@@ -103,11 +103,17 @@
             {expired_after, 48}
 
         ]},
-        %% Session
+        %% Queue
         {queue, [
-            %% Max queue length. enqueued messages when persistent client disconnected, 
+            %% simple | priority
+            {type, simple},
+
+            %% Topic Priority: 0~255, Default is 0
+            %% {priority, [{"topic/1", 10}, {"topic/2", 8}]},
+
+            %% Max queue length. Enqueued messages when persistent client disconnected,
             %% or inflight window is full.
-            {max_length, 100},
+            {max_length, infinity},
 
             %% Low-water mark of queued messages
             {low_watermark, 0.2},
@@ -139,7 +145,7 @@
         %% PubSub and Router
         {pubsub, [
             %% Default should be scheduler numbers
-            %% {pool_size, 8},
+            {pool_size, 8},
             
             %% Subscription: disc | ram | false
             {subscription, ram},

+ 1 - 0
rel/files/vm.args

@@ -46,6 +46,7 @@
 
 -env ERTS_MAX_PORTS 8192
 
+## Mnesia and SSL will create temporary ets tables.
 -env ERL_MAX_ETS_TABLES 1024
 
 ## Tweak GC to run more often

+ 0 - 1
src/emqttd.app.src

@@ -13,4 +13,3 @@
   {mod, {emqttd_app, []}},
   {env, []}
  ]}.
-

+ 20 - 5
src/emqttd.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -23,12 +23,16 @@
 %%%
 %%% @author Feng Lee <feng@emqtt.io>
 %%%-----------------------------------------------------------------------------
+
 -module(emqttd).
 
 -export([start/0, env/1, env/2,
          start_listeners/0, stop_listeners/0,
          load_all_mods/0, is_mod_enabled/1,
-         is_running/1]).
+         is_running/1, seed_now/0]).
+
+%% Utility functions.
+-export([reg_name/2]).
 
 -define(MQTT_SOCKOPTS, [
         binary,
@@ -122,10 +126,8 @@ load_mod({Name, Opts}) ->
 is_mod_enabled(Name) ->
     env(modules, Name) =/= undefined.
 
-%%------------------------------------------------------------------------------
 %% @doc Is running?
-%% @end
-%%------------------------------------------------------------------------------
+-spec is_running(node()) -> boolean().
 is_running(Node) ->
     case rpc:call(Node, erlang, whereis, [?APP]) of
         {badrpc, _}          -> false;
@@ -133,3 +135,16 @@ is_running(Node) ->
         Pid when is_pid(Pid) -> true
     end.
 
+-spec reg_name(module(), pos_integer()) -> atom().
+reg_name(M, Id) when is_atom(M), is_integer(Id) ->
+    list_to_atom(lists:concat([M, "_", Id])).
+
+seed_now() ->
+    case erlang:function_exported(erlang, timestamp, 0) of
+        true -> %% R18
+            random:seed(erlang:timestamp());
+        false ->
+            %% compress 'now()' warning...
+            random:seed(os:timestamp())
+    end.
+

+ 1 - 1
src/emqttd_access_control.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_access_rule.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_acl_internal.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_acl_mod.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_alarm.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_app.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_auth_anonymous.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_auth_clientid.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_auth_ldap.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 23 - 1
src/emqttd_auth_mod.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -27,6 +27,10 @@
 
 -include("emqttd.hrl").
 
+-export([passwd_hash/2]).
+
+-type hash_type() :: plain | md5 | sha | sha256.
+
 %%%=============================================================================
 %%% Auth behavihour
 %%%=============================================================================
@@ -53,3 +57,21 @@ behaviour_info(_Other) ->
 
 -endif.
 
+%% @doc Password Hash
+-spec passwd_hash(hash_type(), binary()) -> binary().
+passwd_hash(plain,  Password)  ->
+    Password;
+passwd_hash(md5,    Password)  ->
+    hexstring(crypto:hash(md5, Password));
+passwd_hash(sha,    Password)  ->
+    hexstring(crypto:hash(sha, Password));
+passwd_hash(sha256, Password)  ->
+    hexstring(crypto:hash(sha256, Password)).
+
+hexstring(<<X:128/big-unsigned-integer>>) ->
+    iolist_to_binary(io_lib:format("~32.16.0b", [X]));
+hexstring(<<X:160/big-unsigned-integer>>) ->
+    iolist_to_binary(io_lib:format("~40.16.0b", [X]));
+hexstring(<<X:256/big-unsigned-integer>>) ->
+    iolist_to_binary(io_lib:format("~64.16.0b", [X])).
+

+ 14 - 5
src/emqttd_auth_username.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -71,6 +71,9 @@ add_user(Username, Password) ->
     User = #?AUTH_USERNAME_TAB{username = Username, password = hash(Password)},
     mnesia:transaction(fun mnesia:write/1, [User]).
 
+add_default_user(Username, Password) ->
+    add_user(bin(Username), bin(Password)).
+
 %%------------------------------------------------------------------------------
 %% @doc Lookup user by username
 %% @end
@@ -98,13 +101,16 @@ all_users() ->
 %%%=============================================================================
 %%% emqttd_auth callbacks
 %%%=============================================================================
-init(Opts) ->
+init(DefautUsers) ->
     mnesia:create_table(?AUTH_USERNAME_TAB, [
             {disc_copies, [node()]},
             {attributes, record_info(fields, ?AUTH_USERNAME_TAB)}]),
     mnesia:add_table_copy(?AUTH_USERNAME_TAB, node(), disc_copies),
+    lists:foreach(fun({Username, Password}) ->
+                add_default_user(Username, Password)
+        end, DefautUsers),
     emqttd_ctl:register_cmd(users, {?MODULE, cli}, []),
-    {ok, Opts}.
+    {ok, []}.
 
 check(#mqtt_client{username = undefined}, _Password, _Opts) ->
     {error, "Username undefined"};
@@ -136,8 +142,11 @@ md5_hash(SaltBin, Password) ->
     erlang:md5(<<SaltBin/binary, Password/binary>>).
 
 salt() ->
-    {A1,A2,A3} = now(),
-    random:seed(A1, A2, A3),
+    emqttd:seed_now(),
     Salt = random:uniform(16#ffffffff),
     <<Salt:32>>.
 
+bin(A) when is_atom(A)   -> bin(atom_to_list(A));
+bin(L) when is_list(L)   -> list_to_binary(L);
+bin(B) when is_binary(B) -> B.
+

+ 2 - 2
src/emqttd_bridge.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -172,7 +172,7 @@ dequeue(State = #state{mqueue = MQ}) ->
         {empty, MQ1} ->
             State#state{mqueue = MQ1};
         {{value, Msg}, MQ1} ->
-            handle_info({dispatch, Msg}, State),
+            handle_info({dispatch, Msg#mqtt_message.topic, Msg}, State),
             dequeue(State#state{mqueue = MQ1})
     end.
 

+ 1 - 1
src/emqttd_bridge_sup.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 2 - 2
src/emqttd_broker.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -218,7 +218,7 @@ stop_tick(TRef) ->
 %%%=============================================================================
 
 init([]) ->
-    random:seed(os:timestamp()),
+    emqttd:seed_now(),
     ets:new(?BROKER_TAB, [set, public, named_table]),
     % Create $SYS Topics
     emqttd_pubsub:create(topic, <<"$SYS/brokers">>),

+ 8 - 9
src/emqttd_cli.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -101,7 +101,7 @@ broker(["metrics"]) ->
             end, lists:sort(emqttd_metrics:all()));
 
 broker(["pubsub"]) ->
-    Pubsubs = supervisor:which_children(emqttd_pubsub_sup),
+    Pubsubs = supervisor:which_children(emqttd_pubsub_sup:pubsub_pool()),
     foreach(fun({{_, Id}, Pid, _, _}) ->
                 ProcInfo = erlang:process_info(Pid, ?PROC_INFOKEYS),
                 ?PRINT("pubsub: ~w~n", [Id]),
@@ -323,7 +323,7 @@ plugins(_) ->
 
 bridges(["list"]) ->
     foreach(fun({{Node, Topic}, _Pid}) ->
-                ?PRINT("bridge: ~s ~s~n", [Node, Topic])
+                ?PRINT("bridge: ~s--~s-->~s~n", [node(), Topic, Node])
             end, emqttd_bridge_sup:bridges());
 
 bridges(["options"]) ->
@@ -449,7 +449,7 @@ trace(_) ->
             {"trace topic <Topic> off",          "stop to trace Topic"}]).
 
 trace_on(Who, Name, LogFile) ->
-    case emqttd_trace:start_trace({Who, bin(Name)}, LogFile) of
+    case emqttd_trace:start_trace({Who, iolist_to_binary(Name)}, LogFile) of
         ok ->
             ?PRINT("trace ~s ~s successfully.~n", [Who, Name]);
         {error, Error} ->
@@ -457,7 +457,7 @@ trace_on(Who, Name, LogFile) ->
     end.
 
 trace_off(Who, Name) ->
-    case emqttd_trace:stop_trace({Who, bin(Name)}) of
+    case emqttd_trace:stop_trace({Who, iolist_to_binary(Name)}) of
         ok -> 
             ?PRINT("stop to trace ~s ~s successfully.~n", [Who, Name]);
         {error, Error} ->
@@ -507,17 +507,16 @@ print({{ClientId, _ClientPid}, SessInfo}) ->
                 awaiting_rel,
                 awaiting_ack,
                 awaiting_comp,
-                created_at,
-                subscriptions],
+                created_at],
     ?PRINT("Session(~s, clean_sess=~s, max_inflight=~w, inflight_queue=~w, "
            "message_queue=~w, message_dropped=~w, "
            "awaiting_rel=~w, awaiting_ack=~w, awaiting_comp=~w, "
-           "created_at=~w, subscriptions=~s)~n",
+           "created_at=~w)~n",
             [ClientId | [format(Key, proplists:get_value(Key, SessInfo)) || Key <- InfoKeys]]).
 
 print(topic, Topic, Records) ->
     Nodes = [Node || #mqtt_topic{node = Node} <- Records],
-    ?PRINT("~s: on ~p~n", [Topic, Nodes]);
+    ?PRINT("~s: ~p~n", [Topic, Nodes]);
 
 print(subscription, ClientId, Subscriptions) ->
     TopicTable = [{Topic, Qos} || #mqtt_subscription{topic = Topic, qos = Qos} <- Subscriptions],

+ 1 - 1
src/emqttd_client.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_cm.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_cm_sup.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_ctl.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_dist.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_gen_mod.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_guid.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_http.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_keepalive.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_log.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_message.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 2 - 2
src/emqttd_metrics.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -283,7 +283,7 @@ key(counter, Metric) ->
 %%%=============================================================================
 
 init([]) ->
-    random:seed(os:timestamp()),
+    emqttd:seed_now(),
     Metrics = ?SYSTOP_BYTES ++ ?SYSTOP_PACKETS ++ ?SYSTOP_MESSAGES,
     % Create metrics table
     ets:new(?METRIC_TAB, [set, public, named_table, {write_concurrency, true}]),

+ 1 - 1
src/emqttd_mnesia.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_mod_presence.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_mod_rewrite.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 5 - 8
src/emqttd_mod_subscription.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -40,7 +40,7 @@
 -endif.
 
 load(Opts) ->
-    Topics = [{bin(Topic), QoS} || {Topic, QoS} <- Opts, ?IS_QOS(QoS)],
+    Topics = [{iolist_to_binary(Topic), QoS} || {Topic, QoS} <- Opts, ?IS_QOS(QoS)],
     State = #state{topics = Topics, stored = lists:member(stored, Opts)},
     emqttd_broker:hook('client.connected', {?MODULE, client_connected},
                        {?MODULE, client_connected, [State]}),
@@ -52,7 +52,9 @@ client_connected(?CONNACK_ACCEPT, #mqtt_client{client_id  = ClientId,
                  #state{topics = Topics, stored = Stored}) ->
     Replace = fun(Topic) -> rep(<<"$u">>, Username, rep(<<"$c">>, ClientId, Topic)) end,
     TopicTable = with_stored(Stored, ClientId, [{Replace(Topic), Qos} || {Topic, Qos} <- Topics]),
-    emqttd_client:subscribe(ClientPid, TopicTable).
+    emqttd_client:subscribe(ClientPid, TopicTable);
+
+client_connected(_ConnAck, _Client, _State) -> ok.
 
 with_stored(false, _ClientId, TopicTable) ->
     TopicTable;
@@ -70,8 +72,3 @@ rep(<<"$u">>, undefined, Topic) ->
 rep(<<"$u">>, Username, Topic) ->
     emqttd_topic:feed_var(<<"$u">>, Username, Topic).
 
-bin(B) when is_binary(B) ->
-    B;
-bin(S) when is_list(S) ->
-    list_to_binary(S).
-

+ 1 - 1
src/emqttd_mod_sup.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 133 - 67
src/emqttd_mqueue.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -30,20 +30,21 @@
 %%%
 %%% If the broker restarted or crashed, all the messages queued will be gone.
 %%% 
-%%% Desgin of The Queue:
+%%% Concept of Message Queue and Inflight Window:
+%%%
 %%%       |<----------------- Max Len ----------------->|
 %%%       -----------------------------------------------
-%%% IN -> |       Pending Messages   | Inflight Window  | -> Out
+%%% IN -> |      Messages Queue   |  Inflight Window    | -> Out
 %%%       -----------------------------------------------
-%%%                                  |<--- Win Size --->|
+%%%                               |<---   Win Size  --->|
 %%%
 %%%
-%%% 1. Inflight Window to store the messages awaiting for ack.
+%%% 1. Inflight Window to store the messages delivered and awaiting for puback.
 %%%
-%%% 2. Suspend IN messages when the queue is deactive, or inflight windows is full.
+%%% 2. Enqueue messages when the inflight window is full.
 %%%
 %%% 3. If the queue is full, dropped qos0 messages if store_qos0 is true,
-%%%    otherwise dropped the oldest pending one.
+%%%    otherwise dropped the oldest one.
 %%%
 %%% @end
 %%%
@@ -55,96 +56,161 @@
 
 -include("emqttd_protocol.hrl").
 
--export([new/3, name/1,
-         is_empty/1, is_full/1,
-         len/1, max_len/1,
-         in/2, out/1,
-         stats/1]).
+-export([new/3, type/1, name/1, is_empty/1, len/1, max_len/1, in/2, out/1, stats/1]).
 
 -define(LOW_WM, 0.2).
 
 -define(HIGH_WM, 0.6).
 
--record(mqueue, {name,
-                 q          = queue:new(), %% pending queue
-                 len        = 0,           %% current queue len
-                 low_wm     = ?LOW_WM,
-                 high_wm    = ?HIGH_WM,
-                 max_len    = ?MAX_LEN,
-                 qos0       = false,
-                 dropped    = 0,
+-type priority() :: {iolist(), pos_integer()}.
+
+-type option() :: {type, simple | priority}
+                | {max_length, pos_integer() | infinity}
+                | {priority, list(priority())}
+                | {low_watermark, float()}     %% Low watermark
+                | {high_watermark, float()}    %% High watermark
+                | {queue_qos0, boolean()}.     %% Queue Qos0?
+
+-type mqueue_option() :: {max_length, pos_integer()} %% Max queue length
+                       | {low_watermark, float()}    %% Low watermark
+                       | {high_watermark, float()}   %% High watermark
+                       | {queue_qos0, boolean()}.    %% Queue Qos0
+
+-type stat() :: {max_len, infinity | pos_integer()}
+              | {len, non_neg_integer()}
+              | {dropped, non_neg_integer()}.
+
+-record(mqueue, {type :: simple | priority,
+                 name, q :: queue:queue() | priority_queue:q(),
+                 %% priority table
+                 pseq = 0, priorities = [],
+                 %% len of simple queue
+                 len = 0, max_len = ?MAX_LEN,
+                 low_wm = ?LOW_WM, high_wm = ?HIGH_WM,
+                 qos0 = false, dropped = 0,
                  alarm_fun}).
 
 -type mqueue() :: #mqueue{}.
 
--type mqueue_option() :: {max_length, pos_integer()}      %% Max queue length
-                       | {low_watermark, float()}         %% Low watermark
-                       | {high_watermark, float()}        %% High watermark
-                       | {queue_qos0, boolean()}.         %% Queue Qos0
-
--export_type([mqueue/0]).
+-export_type([mqueue/0, priority/0, option/0]).
 
-%%------------------------------------------------------------------------------
 %% @doc New Queue.
-%% @end
-%%------------------------------------------------------------------------------
--spec new(binary(), list(mqueue_option()), fun()) -> mqueue().
+-spec new(iolist(), list(mqueue_option()), fun()) -> mqueue().
 new(Name, Opts, AlarmFun) ->
-    MaxLen = emqttd_opts:g(max_length, Opts, 1000),
-    #mqueue{name     = Name,
-            max_len  = MaxLen,
-            low_wm   = round(MaxLen * emqttd_opts:g(low_watermark, Opts, ?LOW_WM)),
-            high_wm  = round(MaxLen * emqttd_opts:g(high_watermark, Opts, ?HIGH_WM)),
-            qos0     = emqttd_opts:g(queue_qos0, Opts, true),
-            alarm_fun = AlarmFun}.
+    Type = emqttd_opts:g(type, Opts, simple),
+    MaxLen = emqttd_opts:g(max_length, Opts, infinity),
+    init_q(#mqueue{type = Type, name = iolist_to_binary(Name),
+                   len = 0, max_len = MaxLen,
+                   low_wm = low_wm(MaxLen, Opts),
+                   high_wm = high_wm(MaxLen, Opts),
+                   qos0 = emqttd_opts:g(queue_qos0, Opts, false),
+                   alarm_fun = AlarmFun}, Opts).
+
+init_q(MQ = #mqueue{type = simple}, _Opts) ->
+    MQ#mqueue{q = queue:new()};
+init_q(MQ = #mqueue{type = priority}, Opts) ->
+    Priorities = emqttd_opts:g(priority, Opts, []),
+    init_p(Priorities, MQ#mqueue{q = priority_queue:new()}).
+
+init_p([], MQ) ->
+    MQ;
+init_p([{Topic, P} | L], MQ) ->
+    {_, MQ1} = insert_p(iolist_to_binary(Topic), P, MQ),
+    init_p(L, MQ1).
+
+insert_p(Topic, P, MQ = #mqueue{priorities = Tab, pseq = Seq}) ->
+    <<PInt:48>> = <<P:8, (erlang:phash2(Topic)):32, Seq:8>>,
+    {PInt, MQ#mqueue{priorities = [{Topic, PInt} | Tab], pseq = Seq + 1}}.
+
+low_wm(infinity, _Opts) ->
+    infinity;
+low_wm(MaxLen, Opts) ->
+    round(MaxLen * emqttd_opts:g(low_watermark, Opts, ?LOW_WM)).
+
+high_wm(infinity, _Opts) ->
+    infinity;
+high_wm(MaxLen, Opts) ->
+    round(MaxLen * emqttd_opts:g(high_watermark, Opts, ?HIGH_WM)).
 
+-spec name(mqueue()) -> iolist().
 name(#mqueue{name = Name}) ->
     Name.
 
-is_empty(#mqueue{len = 0}) -> true;
-is_empty(_MQ)              -> false.
+-spec type(mqueue()) -> atom().
+type(#mqueue{type = Type}) ->
+    Type.
 
-is_full(#mqueue{len = Len, max_len = MaxLen})
-    when Len =:= MaxLen -> true;
-is_full(_MQ) -> false.
+is_empty(#mqueue{type = simple, len = Len}) -> Len =:= 0;
+is_empty(#mqueue{type = priority, q = Q})   -> priority_queue:is_empty(Q).
 
-len(#mqueue{len = Len}) -> Len.
+len(#mqueue{type = simple, len = Len}) -> Len;
+len(#mqueue{type = priority, q = Q})   -> priority_queue:len(Q).
 
 max_len(#mqueue{max_len= MaxLen}) -> MaxLen.
 
-stats(#mqueue{max_len = MaxLen, len = Len, dropped = Dropped}) ->
-    [{max_len, MaxLen}, {len, Len}, {dropped, Dropped}].
-
-%%------------------------------------------------------------------------------
-%% @doc Queue one message.
-%% @end
-%%------------------------------------------------------------------------------
+%% @doc Stats of the mqueue
+-spec stats(mqueue()) -> [stat()].
+stats(#mqueue{type = Type, q = Q, max_len = MaxLen, len = Len, dropped = Dropped}) ->
+    [{len, case Type of
+                simple   -> Len;
+                priority -> priority_queue:len(Q)
+            end} | [{max_len, MaxLen}, {dropped, Dropped}]].
 
+%% @doc Enqueue a message.
 -spec in(mqtt_message(), mqueue()) -> mqueue().
-%% drop qos0
 in(#mqtt_message{qos = ?QOS_0}, MQ = #mqueue{qos0 = false}) ->
     MQ;
-
-%% simply drop the oldest one if queue is full, improve later
-in(Msg, MQ = #mqueue{q = Q, len = Len, max_len = MaxLen, dropped = Dropped})
-    when Len =:= MaxLen ->
-    {{value, _OldMsg}, Q2} = queue:out(Q),
-    %lager:error("MQueue(~s) drop ~s", [Name, emqttd_message:format(OldMsg)]),
+in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len, max_len = infinity}) ->
+    MQ#mqueue{q = queue:in(Msg, Q), len = Len + 1};
+in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len, max_len = MaxLen, dropped = Dropped})
+    when Len >= MaxLen ->
+    {{value, _Old}, Q2} = queue:out(Q),
     MQ#mqueue{q = queue:in(Msg, Q2), dropped = Dropped +1};
-
-in(Msg, MQ = #mqueue{q = Q, len = Len}) ->
-    maybe_set_alarm(MQ#mqueue{q = queue:in(Msg, Q), len = Len + 1}).
-
-out(MQ = #mqueue{len = 0}) ->
+in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len}) ->
+    maybe_set_alarm(MQ#mqueue{q = queue:in(Msg, Q), len = Len + 1});
+
+in(Msg = #mqtt_message{topic = Topic}, MQ = #mqueue{type = priority, q = Q,
+                                                    priorities = Priorities,
+                                                    max_len = infinity}) ->
+    case lists:keysearch(Topic, 1, Priorities) of
+        {value, {_, Pri}} ->
+            MQ#mqueue{q = priority_queue:in(Msg, Pri, Q)};
+        false ->
+            {Pri, MQ1} = insert_p(Topic, 0, MQ),
+            MQ1#mqueue{q = priority_queue:in(Msg, Pri, Q)}
+    end;
+in(Msg = #mqtt_message{topic = Topic}, MQ = #mqueue{type = priority, q = Q,
+                                                    priorities = Priorities,
+                                                    max_len = MaxLen}) ->
+    case lists:keysearch(Topic, 1, Priorities) of
+        {value, {_, Pri}} ->
+            case priority_queue:plen(Pri, Q) >= MaxLen of
+                true ->
+                    {_, Q1} = priority_queue:out(Pri, Q),
+                    MQ#mqueue{q = priority_queue:in(Msg, Pri, Q1)};
+                false ->
+                    MQ#mqueue{q = priority_queue:in(Msg, Pri, Q)}
+            end;
+        false ->
+            {Pri, MQ1} = insert_p(Topic, 0, MQ),
+            MQ1#mqueue{q = priority_queue:in(Msg, Pri, Q)}
+    end.
+
+out(MQ = #mqueue{type = simple, len = 0}) ->
     {empty, MQ};
-
-out(MQ = #mqueue{q = Q, len = Len}) ->
-    {Result, Q2} = queue:out(Q),
-    {Result, maybe_clear_alarm(MQ#mqueue{q = Q2, len = Len - 1})}.
+out(MQ = #mqueue{type = simple, q = Q, len = Len, max_len = infinity}) ->
+    {R, Q2} = queue:out(Q),
+    {R, MQ#mqueue{q = Q2, len = Len - 1}};
+out(MQ = #mqueue{type = simple, q = Q, len = Len}) ->
+    {R, Q2} = queue:out(Q),
+    {R, maybe_clear_alarm(MQ#mqueue{q = Q2, len = Len - 1})};
+out(MQ = #mqueue{type = priority, q = Q}) ->
+    {R, Q2} = priority_queue:out(Q),
+    {R, MQ#mqueue{q = Q2}}.
 
 maybe_set_alarm(MQ = #mqueue{name = Name, len = Len, high_wm = HighWM, alarm_fun = AlarmFun})
     when Len > HighWM ->
-    Alarm = #mqtt_alarm{id = list_to_binary(["queue_high_watermark.", Name]),
+    Alarm = #mqtt_alarm{id = iolist_to_binary(["queue_high_watermark.", Name]),
                         severity = warning,
                         title = io_lib:format("Queue ~s high-water mark", [Name]),
                         summary = io_lib:format("queue len ~p > high_watermark ~p", [Len, HighWM])},

+ 1 - 1
src/emqttd_net.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_opts.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_packet.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_parser.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_plugins.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_pool_sup.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 2 - 5
src/emqttd_pooler.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -53,10 +53,7 @@ start_link() ->
 %%%=============================================================================
 -spec start_link(atom(), pos_integer()) -> {ok, pid()} | ignore | {error, any()}.
 start_link(Pool, Id) ->
-    gen_server:start_link({local, name(Id)}, ?MODULE, [Pool, Id], []).
-
-name(Id) ->
-    list_to_atom(lists:concat([?MODULE, "_", integer_to_list(Id)])).
+    gen_server:start_link({local, emqttd:reg_name(?MODULE, Id)}, ?MODULE, [Pool, Id], []).
 
 %%------------------------------------------------------------------------------
 %% @doc Submit work to pooler

+ 1 - 1
src/emqttd_protocol.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 82 - 67
src/emqttd_pubsub.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -19,7 +19,7 @@
 %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 %%% SOFTWARE.
 %%%-----------------------------------------------------------------------------
-%%% @doc emqttd pubsub
+%%% @doc PubSub
 %%%
 %%% @author Feng Lee <feng@emqtt.io>
 %%%-----------------------------------------------------------------------------
@@ -43,9 +43,7 @@
 -export([start_link/4]).
 
 -export([create/2, lookup/2, subscribe/1, subscribe/2,
-         unsubscribe/1, unsubscribe/2, publish/1, delete/2]).
-
-%% Subscriptions API
+         publish/1, unsubscribe/1, unsubscribe/2, delete/2]).
 
 %% Local node
 -export([match/1]).
@@ -62,8 +60,6 @@
 
 -define(ROUTER, emqttd_router).
 
--define(HELPER, emqttd_pubsub_helper).
-
 %%%=============================================================================
 %%% Mnesia callbacks
 %%%=============================================================================
@@ -123,26 +119,19 @@ cache_env(Key) ->
 %%% API
 %%%=============================================================================
 
-%%------------------------------------------------------------------------------
 %% @doc Start one pubsub server
-%% @end
-%%------------------------------------------------------------------------------
 -spec start_link(Pool, Id, StatsFun, Opts) -> {ok, pid()} | ignore | {error, any()} when
     Pool     :: atom(),
     Id       :: pos_integer(),
-    StatsFun :: fun(),
+    StatsFun :: fun((atom()) -> any()),
     Opts     :: list(tuple()).
 start_link(Pool, Id, StatsFun, Opts) ->
-    gen_server2:start_link({local, name(Id)}, ?MODULE, [Pool, Id, StatsFun, Opts], []).
+    gen_server2:start_link({local, emqttd:reg_name(?MODULE, Id)},
+                           ?MODULE, [Pool, Id, StatsFun, Opts], []).
 
-name(Id) ->
-    list_to_atom("emqttd_pubsub_" ++ integer_to_list(Id)).
-
-%%------------------------------------------------------------------------------
 %% @doc Create Topic or Subscription.
-%% @end
-%%------------------------------------------------------------------------------
--spec create(topic | subscription, binary() | {binary(), binary(), mqtt_qos()}) -> ok | {error, any()}.
+-spec create(topic, emqttd_topic:topic()) -> ok | {error, any()};
+            (subscription, {binary(), binary(), mqtt_qos()}) -> ok | {error, any()}.
 create(topic, Topic) when is_binary(Topic) ->
     Record = #mqtt_topic{topic = Topic, node = node()},
     case mnesia:transaction(fun add_topic/1, [Record]) of
@@ -151,39 +140,33 @@ create(topic, Topic) when is_binary(Topic) ->
     end;
 
 create(subscription, {SubId, Topic, Qos}) when is_binary(SubId) andalso is_binary(Topic) ->
-    case mnesia:transaction(fun add_subscription/2, [SubId, {Topic, Qos}]) of
+    case mnesia:transaction(fun add_subscription/2, [SubId, {Topic, ?QOS_I(Qos)}]) of
         {atomic, ok}     -> ok;
         {aborted, Error} -> {error, Error}
     end.
 
-%%------------------------------------------------------------------------------
 %% @doc Lookup Topic or Subscription.
-%% @end
-%%------------------------------------------------------------------------------
--spec lookup(topic | subscription, binary()) -> list().
-lookup(topic, Topic) ->
+-spec lookup(topic, emqttd_topic:topic()) -> list(mqtt_topic());
+            (subscription, binary())      -> list(mqtt_subscription()).
+lookup(topic, Topic) when is_binary(Topic) ->
     mnesia:dirty_read(topic, Topic);
 
-lookup(subscription, ClientId) ->
-    mnesia:dirty_read(subscription, ClientId).
+lookup(subscription, SubId) when is_binary(SubId) ->
+    mnesia:dirty_read(subscription, SubId).
 
-%%------------------------------------------------------------------------------
 %% @doc Delete Topic or Subscription.
-%% @end
-%%------------------------------------------------------------------------------
+-spec delete(topic, emqttd_topic:topic()) -> ok | {error, any()};
+            (subscription, binary() | {binary(), emqttd_topic:topic()}) -> ok.
 delete(topic, _Topic) ->
     {error, unsupported};
 
-delete(subscription, ClientId) when is_binary(ClientId) ->
-    mnesia:dirty_delete({subscription, ClientId});
+delete(subscription, SubId) when is_binary(SubId) ->
+    mnesia:dirty_delete({subscription, SubId});
 
-delete(subscription, {ClientId, Topic}) when is_binary(ClientId) ->
-    mnesia:async_dirty(fun remove_subscriptions/2, [ClientId, [Topic]]).
+delete(subscription, {SubId, Topic}) when is_binary(SubId) andalso is_binary(Topic) ->
+    mnesia:async_dirty(fun remove_subscriptions/2, [SubId, [Topic]]).
 
-%%------------------------------------------------------------------------------
 %% @doc Subscribe Topics
-%% @end
-%%------------------------------------------------------------------------------
 -spec subscribe({Topic, Qos} | list({Topic, Qos})) ->
     {ok, Qos | list(Qos)} | {error, any()} when
     Topic   :: binary(),
@@ -206,34 +189,28 @@ subscribe(ClientId, TopicTable) when is_binary(ClientId) andalso is_list(TopicTa
 fixqos(TopicTable) ->
     [{Topic, ?QOS_I(Qos)} || {Topic, Qos} <- TopicTable].
 
-call(Request) ->
-    PubSub = gproc_pool:pick_worker(pubsub, self()),
-    gen_server2:call(PubSub, Request, infinity).
-
-%%------------------------------------------------------------------------------
 %% @doc Unsubscribe Topic or Topics
-%% @end
-%%------------------------------------------------------------------------------
--spec unsubscribe(binary() | list(binary())) -> ok.
+-spec unsubscribe(emqttd_topic:topic() | list(emqttd_topic:topic())) -> ok.
 unsubscribe(Topic) when is_binary(Topic) ->
     unsubscribe([Topic]);
 unsubscribe(Topics = [Topic|_]) when is_binary(Topic) ->
     cast({unsubscribe, {undefined, self()}, Topics}).
 
--spec unsubscribe(binary(), binary() | list(binary())) -> ok.
+-spec unsubscribe(binary(), emqttd_topic:topic() | list(emqttd_topic:topic())) -> ok.
 unsubscribe(ClientId, Topic) when is_binary(ClientId) andalso is_binary(Topic) ->
     unsubscribe(ClientId, [Topic]);
 unsubscribe(ClientId, Topics = [Topic|_]) when is_binary(Topic) ->
     cast({unsubscribe, {ClientId, self()}, Topics}).
 
+call(Request) ->
+    gen_server2:call(pick(self()), Request, infinity).
+
 cast(Msg) ->
-    PubSub = gproc_pool:pick_worker(pubsub, self()),
-    gen_server2:cast(PubSub, Msg).
+    gen_server2:cast(pick(self()), Msg).
+
+pick(Self) -> gproc_pool:pick_worker(pubsub, Self).
 
-%%------------------------------------------------------------------------------
 %% @doc Publish to cluster nodes
-%% @end
-%%------------------------------------------------------------------------------
 -spec publish(Msg :: mqtt_message()) -> ok.
 publish(Msg = #mqtt_message{from = From}) ->
     trace(publish, From, Msg),
@@ -257,35 +234,41 @@ publish(To, Msg) ->
                     end
                   end, match(To)).
 
-%%------------------------------------------------------------------------------
 %% @doc Match Topic Name with Topic Filters
-%% @end
-%%------------------------------------------------------------------------------
--spec match(binary()) -> [mqtt_topic()].
+-spec match(emqttd_topic:topic()) -> [mqtt_topic()].
 match(To) ->
     MatchedTopics = mnesia:async_dirty(fun emqttd_trie:match/1, [To]),
-    %% ets:lookup for topic table will be replicated.
+    %% ets:lookup for topic table will be replicated to all nodes.
     lists:append([ets:lookup(topic, Topic) || Topic <- MatchedTopics]).
 
 %%%=============================================================================
 %%% gen_server callbacks
 %%%=============================================================================
 
-init([Pool, Id, StatsFun, Opts]) ->
-    ?ROUTER:init(Opts),
+init([Pool, Id, StatsFun, _Opts]) ->
     ?GPROC_POOL(join, Pool, Id),
     {ok, #state{pool = Pool, id = Id, statsfun = StatsFun}}.
 
 handle_call({subscribe, {SubId, SubPid}, TopicTable}, _From,
             State = #state{statsfun = StatsFun}) ->
 
+    %% Monitor SubPid first
+    try_monitor(SubPid),
+
+    %% Topics
     Topics = [Topic || {Topic, _Qos} <- TopicTable],
 
-    %% Add routes first
-    ?ROUTER:add_routes(Topics, SubPid),
+    NewTopics = Topics -- reverse_routes(SubPid),
+
+    %% Add routes
+    ?ROUTER:add_routes(NewTopics, SubPid),
+
+    insert_reverse_routes(SubPid, NewTopics),
 
-    %% Insert topic records to global topic table
-    Records = [#mqtt_topic{topic = Topic, node = node()} || Topic <- Topics],
+    StatsFun(reverse_route),
+
+    %% Insert topic records to mnesia
+    Records = [#mqtt_topic{topic = Topic, node = node()} || Topic <- NewTopics],
 
     case mnesia:transaction(fun add_topics/1, [Records]) of
         {atomic, _} ->
@@ -307,9 +290,14 @@ handle_call(Req, _From, State) ->
    ?UNEXPECTED_REQ(Req, State).
 
 handle_cast({unsubscribe, {SubId, SubPid}, Topics}, State = #state{statsfun = StatsFun}) ->
+
     %% Delete routes first
     ?ROUTER:delete_routes(Topics, SubPid),
 
+    delete_reverse_routes(SubPid, Topics),
+
+    StatsFun(reverse_route),
+
     %% Remove subscriptions
     if_subscription(
         fun(_) ->
@@ -317,19 +305,21 @@ handle_cast({unsubscribe, {SubId, SubPid}, Topics}, State = #state{statsfun = St
             emqttd_pooler:async_submit({mnesia, async_dirty, Args}),
             StatsFun(subscription)
         end),
+
     {noreply, State};
 
 handle_cast(Msg, State) ->
     ?UNEXPECTED_MSG(Msg, State).
 
-handle_info({'DOWN', _Mon, _Type, DownPid, _Info}, State) ->
+handle_info({'DOWN', _Mon, _Type, DownPid, _Info}, State = #state{statsfun = StatsFun}) ->
 
-    Routes = ?ROUTER:lookup_routes(DownPid),
+    Topics = reverse_routes(DownPid),
 
-    %% Delete all routes of the process
-    ?ROUTER:delete_routes(DownPid),
+    ?ROUTER:delete_routes(Topics, DownPid),
 
-    ?HELPER:aging([Topic || Topic <- Routes, not ?ROUTER:has_route(Topic)]),
+    delete_reverse_routes(DownPid),
+
+    StatsFun(reverse_route),
 
     {noreply, State, hibernate};
 
@@ -395,6 +385,31 @@ remove_subscriptions(SubId, Topics) ->
 delete_subscription(Record) ->
     mnesia:delete_object(subscription, Record, write).
 
+reverse_routes(SubPid) ->
+    case ets:member(reverse_route, SubPid) of
+        true  ->
+            try ets:lookup_element(reverse_route, SubPid, 2) catch error:badarg -> [] end;
+        false ->
+            []
+    end.
+
+insert_reverse_routes(SubPid, Topics) ->
+    ets:insert(reverse_route, [{SubPid, Topic} || Topic <- Topics]).
+
+delete_reverse_routes(SubPid, Topics) ->
+    lists:foreach(fun(Topic) ->
+                ets:delete_object(reverse_route, {SubPid, Topic})
+        end, Topics).
+
+delete_reverse_routes(SubPid) ->
+    ets:delete(reverse_route, SubPid).
+
+try_monitor(SubPid) ->
+    case ets:member(reverse_route, SubPid) of
+        true  -> ignore;
+        false -> erlang:monitor(process, SubPid)
+    end.
+
 %%%=============================================================================
 %%% Trace Functions
 %%%=============================================================================

+ 16 - 126
src/emqttd_pubsub_helper.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -19,176 +19,66 @@
 %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 %%% SOFTWARE.
 %%%-----------------------------------------------------------------------------
-%%% @doc PubSub Route Aging Helper
+%%% @doc PubSub Helper.
 %%%
 %%% @author Feng Lee <feng@emqtt.io>
 %%%-----------------------------------------------------------------------------
 -module(emqttd_pubsub_helper).
 
--behaviour(gen_server2).
+-behaviour(gen_server).
 
 -include("emqttd.hrl").
 
 -include("emqttd_internal.hrl").
 
 %% API Function Exports
--export([start_link/2, aging/1]).
+-export([start_link/1]).
 
 %% gen_server Function Exports
 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
          terminate/2, code_change/3]).
 
--ifdef(TEST).
--compile(export_all).
--endif.
-
--record(aging, {topics, time, tref}).
-
--record(state, {aging :: #aging{}, statsfun}).
+-record(state, {statsfun}).
 
 -define(SERVER, ?MODULE).
 
--define(ROUTER, emqttd_router).
-
-%%%=============================================================================
-%%% API
-%%%=============================================================================
-
-%%------------------------------------------------------------------------------
-%% @doc Start pubsub helper.
-%% @end
-%%------------------------------------------------------------------------------
--spec start_link(fun(), list(tuple())) -> {ok, pid()} | ignore | {error, any()}.
-start_link(StatsFun, Opts) ->
-    gen_server2:start_link({local, ?SERVER}, ?MODULE, [StatsFun, Opts], []).
-
-%%------------------------------------------------------------------------------
-%% @doc Aging topics
-%% @end
-%%------------------------------------------------------------------------------
--spec aging(list(binary())) -> ok.
-aging(Topics) ->
-    gen_server2:cast(?SERVER, {aging, Topics}).
+%% @doc Start PubSub Helper.
+-spec start_link(fun()) -> {ok, pid()} | ignore | {error, any()}.
+start_link(StatsFun) ->
+    gen_server:start_link({local, ?SERVER}, ?MODULE, [StatsFun], []).
 
 %%%=============================================================================
 %%% gen_server callbacks
 %%%=============================================================================
 
-init([StatsFun, Opts]) ->
+init([StatsFun]) ->
     mnesia:subscribe(system),
-
-    AgingSecs = proplists:get_value(route_aging, Opts, 5),
-
-    %% Aging Timer
-    {ok, AgingTref} = start_tick(AgingSecs div 2),
-
-    {ok, #state{aging = #aging{topics = dict:new(),
-                               time   = AgingSecs,
-                               tref   = AgingTref},
-               statsfun = StatsFun}}.
-
-start_tick(Secs) ->
-    timer:send_interval(timer:seconds(Secs), {clean, aged}).
+    {ok, #state{statsfun = StatsFun}}.
 
 handle_call(Req, _From, State) ->
     ?UNEXPECTED_REQ(Req, State).
 
-handle_cast({aging, Topics}, State = #state{aging = Aging}) ->
-    #aging{topics = Dict} = Aging,
-    TS = emqttd_util:now_to_secs(),
-    Dict1 =
-    lists:foldl(fun(Topic, Acc) ->
-                    case dict:find(Topic, Acc) of
-                        {ok, _} -> Acc;
-                        error   -> dict:store(Topic, TS, Acc)
-                    end
-                end, Dict, Topics),
-    {noreply, State#state{aging = Aging#aging{topics = Dict1}}};
-
 handle_cast(Msg, State) ->
     ?UNEXPECTED_MSG(Msg, State).
 
-handle_info({clean, aged}, State = #state{aging = Aging}) ->
-
-    #aging{topics = Dict, time = Time} = Aging,
-
-    ByTime  = emqttd_util:now_to_secs() - Time,
-
-    Dict1 = try_clean(ByTime, dict:to_list(Dict)),
-
-    NewAging = Aging#aging{topics = dict:from_list(Dict1)},
-
-    noreply(State#state{aging = NewAging});
-
 handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
-    %% mnesia master?
+    %% TODO: mnesia master?
     Pattern = #mqtt_topic{_ = '_', node = Node},
     F = fun() ->
             [mnesia:delete_object(topic, R, write) ||
                 R <- mnesia:match_object(topic, Pattern, write)]
         end,
-    mnesia:async_dirty(F),
-    noreply(State);
+    mnesia:transaction(F), noreply(State);
 
 handle_info(Info, State) ->
     ?UNEXPECTED_INFO(Info, State).
 
-terminate(_Reason, #state{aging = #aging{tref = TRef}}) ->
-    timer:cancel(TRef).
+terminate(_Reason, _State) ->
+    mnesia:unsubscribe(system).
 
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%%=============================================================================
-%%% Internal Functions
-%%%=============================================================================
-
 noreply(State = #state{statsfun = StatsFun}) ->
-    StatsFun(topic),
-    {noreply, State, hibernate}.
-
-try_clean(ByTime, List) ->
-    try_clean(ByTime, List, []).
-
-try_clean(_ByTime, [], Acc) ->
-    Acc;
-
-try_clean(ByTime, [{Topic, TS} | Left], Acc) ->
-    case ?ROUTER:has_route(Topic) of
-        false ->
-            try_clean2(ByTime, {Topic, TS}, Left, Acc);
-        true  ->
-            try_clean(ByTime, Left, Acc)
-    end.
-
-try_clean2(ByTime, {Topic, TS}, Left, Acc) when TS > ByTime ->
-    try_clean(ByTime, Left, [{Topic, TS}|Acc]);
-
-try_clean2(ByTime, {Topic, _TS}, Left, Acc) ->
-    TopicR = #mqtt_topic{topic = Topic, node = node()},
-    mnesia:transaction(fun try_remove_topic/1, [TopicR]),
-    try_clean(ByTime, Left, Acc).
-
-try_remove_topic(TopicR = #mqtt_topic{topic = Topic}) ->
-    %% Lock topic first
-    case mnesia:wread({topic, Topic}) of
-        [] -> ok;
-        [TopicR] ->
-            if_no_route(Topic, fun() ->
-                %% Remove topic and trie
-                mnesia:delete_object(topic, TopicR, write),
-                emqttd_trie:delete(Topic)
-            end);
-        _More ->
-            if_no_route(Topic, fun() ->
-                %% Remove topic
-                mnesia:delete_object(topic, TopicR, write)
-            end)
-    end.
-        
-if_no_route(Topic, Fun) ->
-    case ?ROUTER:has_route(Topic) of
-        true -> ok;
-        false -> Fun()
-    end.
+    StatsFun(topic), {noreply, State}.
 

+ 49 - 14
src/emqttd_pubsub_sup.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -19,7 +19,7 @@
 %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 %%% SOFTWARE.
 %%%-----------------------------------------------------------------------------
-%%% @doc PubSub Supervisor
+%%% @doc PubSub Supervisor.
 %%%
 %%% @author Feng Lee <feng@emqtt.io>
 %%%-----------------------------------------------------------------------------
@@ -31,8 +31,10 @@
 
 -define(HELPER, emqttd_pubsub_helper).
 
+-define(CONCURRENCY_OPTS, [{read_concurrency, true}, {write_concurrency, true}]).
+
 %% API
--export([start_link/0]).
+-export([start_link/0, pubsub_pool/0]).
 
 %% Supervisor callbacks
 -export([init/1]).
@@ -40,24 +42,57 @@
 start_link() ->
     supervisor:start_link({local, ?MODULE}, ?MODULE, [emqttd_broker:env(pubsub)]).
 
-init([Opts]) ->
+pubsub_pool() ->
+    hd([Pid|| {pubsub_pool, Pid, _, _} <- supervisor:which_children(?MODULE)]).
+
+init([Env]) ->
+    %% Create tabs
+    create_tab(route), create_tab(reverse_route),
+
     %% PubSub Helper
-    Helper = {helper, {?HELPER, start_link, [fun stats/1, Opts]},
+    Helper = {helper, {?HELPER, start_link, [fun setstats/1]},
                 permanent, infinity, worker, [?HELPER]},
 
+    %% Router Pool Sup
+    RouterMFA = {emqttd_router, start_link, [fun setstats/1, Env]},
+    %% Pool_size / 2
+    RouterSup = emqttd_pool_sup:spec(router_pool, [router, hash, pool_size(Env) div 2, RouterMFA]),
+
     %% PubSub Pool Sup
-    MFA = {emqttd_pubsub, start_link, [fun stats/1, Opts]},
-    PoolSup = emqttd_pool_sup:spec([pubsub, hash, pool_size(Opts), MFA]),
-    {ok, {{one_for_all, 10, 60}, [Helper, PoolSup]}}.
+    PubSubMFA = {emqttd_pubsub, start_link, [fun setstats/1, Env]},
+    PubSubSup = emqttd_pool_sup:spec(pubsub_pool, [pubsub, hash, pool_size(Env), PubSubMFA]),
+
+    {ok, {{one_for_all, 10, 60}, [Helper, RouterSup, PubSubSup]}}.
+
+create_tab(route) ->
+    %% Route Table: Topic -> Pid1, Pid2, ..., PidN
+    %% duplicate_bag: o(1) insert
+    ensure_tab(route, [public, named_table, duplicate_bag | ?CONCURRENCY_OPTS]);
 
-pool_size(Opts) ->
+create_tab(reverse_route) ->
+    %% Reverse Route Table: Pid -> Topic1, Topic2, ..., TopicN
+    ensure_tab(reverse_route, [public, named_table, bag | ?CONCURRENCY_OPTS]).
+
+ensure_tab(Tab, Opts) ->
+    case ets:info(Tab, name) of
+        undefined -> ets:new(Tab, Opts);
+        _ -> ok
+    end.
+
+pool_size(Env) ->
     Schedulers = erlang:system_info(schedulers),
-    proplists:get_value(pool_size, Opts, Schedulers).
+    proplists:get_value(pool_size, Env, Schedulers).
+
+setstats(route) ->
+    emqttd_stats:setstat('routes/count', ets:info(route, size));
+
+setstats(reverse_route) ->
+    emqttd_stats:setstat('routes/reverse', ets:info(reverse_route, size));
+
+setstats(topic) ->
+    emqttd_stats:setstats('topics/count', 'topics/max', mnesia:table_info(topic, size));
 
-stats(topic) ->
-    emqttd_stats:setstats('topics/count', 'topics/max',
-                          mnesia:table_info(topic, size));
-stats(subscription) ->
+setstats(subscription) ->
     emqttd_stats:setstats('subscriptions/count', 'subscriptions/max',
                           mnesia:table_info(subscription, size)).
 

+ 1 - 1
src/emqttd_retainer.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 229 - 129
src/emqttd_router.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -19,167 +19,267 @@
 %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 %%% SOFTWARE.
 %%%-----------------------------------------------------------------------------
-%%% @doc MQTT Message Router on Local Node
-%%%
-%%% Route Table:
-%%%
-%%%   Topic -> Pid1, Pid2, ...
-%%%
-%%% Reverse Route Table:
-%%%
-%%%   Pid -> Topic1, Topic2, ...
-%%%
-%%% @end
+%%% @doc Message Router on local node.
 %%%
 %%% @author Feng Lee <feng@emqtt.io>
 %%%-----------------------------------------------------------------------------
+
 -module(emqttd_router).
 
+-behaviour(gen_server2).
+
 -include("emqttd.hrl").
 
 -include("emqttd_protocol.hrl").
 
--export([init/1, route/2, lookup_routes/1, has_route/1,
-         add_routes/2, delete_routes/1, delete_routes/2]).
-
--ifdef(TEST).
--compile(export_all).
--endif.
-
-%%------------------------------------------------------------------------------
-%% @doc Create route tables.
-%% @end
-%%------------------------------------------------------------------------------
-init(_Opts) ->
-    TabOpts = [bag, public, named_table,
-               {write_concurrency, true}],
-    %% Route Table: Topic -> {Pid, QoS}
-    %% Route Shard: {Topic, Shard} -> {Pid, QoS}
-    ensure_tab(route, TabOpts),
-
-    %% Reverse Route Table: Pid -> {Topic, QoS}
-    ensure_tab(reverse_route, TabOpts).
-
-ensure_tab(Tab, Opts) ->
-    case ets:info(Tab, name) of
-        undefined ->
-            ets:new(Tab, Opts);
-        _ ->
-            ok
-    end.
+-include("emqttd_internal.hrl").
 
--ifdef(TEST).
-destory() ->
-    ets:delete(route),
-    ets:delete(reverse_route).
--endif.
+-export([start_link/4]).
 
-%%------------------------------------------------------------------------------
-%% @doc Add Routes.
-%% @end
-%%------------------------------------------------------------------------------
--spec add_routes(list(binary()), pid()) -> ok.
-add_routes(Topics, Pid) when is_pid(Pid) ->
-    with_stats(fun() ->
-        case lookup_routes(Pid) of
-            [] ->
-                erlang:monitor(process, Pid),
-                insert_routes(Topics, Pid);
-            InEts ->
-                insert_routes(Topics -- InEts, Pid)
-        end
-    end).
-
-%%------------------------------------------------------------------------------
-%% @doc Lookup Routes
-%% @end
-%%------------------------------------------------------------------------------
--spec lookup_routes(pid()) -> list(binary()).
-lookup_routes(Pid) when is_pid(Pid) ->
-    [Topic || {_, Topic} <- ets:lookup(reverse_route, Pid)].
+%% Route API
+-export([route/2]).
 
-%%------------------------------------------------------------------------------
-%% @doc Has Route?
-%% @end
-%%------------------------------------------------------------------------------
--spec has_route(binary()) -> boolean().
-has_route(Topic) ->
-    ets:member(route, Topic).
+%% Route Admin API
+-export([add_route/2, lookup_routes/1, has_route/1, delete_route/2]).
 
-%%------------------------------------------------------------------------------
-%% @doc Delete Routes
-%% @end
-%%------------------------------------------------------------------------------
--spec delete_routes(list(binary()), pid()) -> ok.
-delete_routes(Topics, Pid) ->
-    with_stats(fun() ->
-        Routes = [{Topic, Pid} || Topic <- Topics],
-        lists:foreach(fun delete_route/1, Routes)
-    end).
-
--spec delete_routes(pid()) -> ok.
-delete_routes(Pid) when is_pid(Pid) ->
-    with_stats(fun() ->
-        Routes = [{Topic, Pid} || Topic <- lookup_routes(Pid)],
-        ets:delete(reverse_route, Pid),
-        lists:foreach(fun delete_route_only/1, Routes)
-    end).
-
-%%------------------------------------------------------------------------------
-%% @doc Route Message on Local Node.
-%% @end
-%%------------------------------------------------------------------------------
--spec route(binary(), mqtt_message()) -> non_neg_integer().
+%% Batch API
+-export([add_routes/2, delete_routes/2]).
+
+%% For Test
+-export([stop/1]).
+
+%% gen_server Callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+         terminate/2, code_change/3]).
+
+-record(aging, {topics, time, tref}).
+
+-record(state, {pool, id, statsfun, aging :: #aging{}}).
+
+%% @doc Start a local router.
+-spec start_link(atom(), pos_integer(), fun((atom()) -> ok), list()) -> {ok, pid()} | {error, any()}.
+start_link(Pool, Id, StatsFun, Env) ->
+    gen_server2:start_link({local, emqttd:reg_name(?MODULE,Id)},
+                           ?MODULE, [Pool, Id, StatsFun, Env], []).
+
+%% @doc Route Message on the local node.
+-spec route(emqttd_topic:topic(), mqtt_message()) -> any().
 route(Queue = <<"$Q/", _Q>>, Msg) ->
-    case ets:lookup(route, Queue) of
+    case lookup_routes(Queue) of
         [] ->
             emqttd_metrics:inc('messages/dropped');
+        [SubPid] ->
+            SubPid ! {dispatch, Queue, Msg};
         Routes ->
             Idx = crypto:rand_uniform(1, length(Routes) + 1),
-            {_, SubPid} = lists:nth(Idx, Routes),
-            dispatch(SubPid, Queue, Msg)
+            SubPid = lists:nth(Idx, Routes),
+            SubPid ! {dispatch, Queue, Msg}
     end;
 
 route(Topic, Msg) ->
-    case ets:lookup(route, Topic) of
+    case lookup_routes(Topic) of
         [] ->
             emqttd_metrics:inc('messages/dropped');
+        [SubPid] -> %% optimize
+            SubPid ! {dispatch, Topic, Msg};
         Routes ->
-            lists:foreach(fun({_Topic, SubPid}) ->
-                            dispatch(SubPid, Topic, Msg)
-                          end, Routes)
+            lists:foreach(fun(SubPid) ->
+                SubPid ! {dispatch, Topic, Msg}
+            end, Routes)
     end.
 
-dispatch(SubPid, Topic, Msg) -> SubPid ! {dispatch, Topic, Msg}.
+%% @doc Has Route?
+-spec has_route(emqttd_topic:topic()) -> boolean().
+has_route(Topic) ->
+    ets:member(route, Topic).
+
+%% @doc Lookup Routes
+-spec lookup_routes(emqttd_topic:topic()) -> list(pid()).
+lookup_routes(Topic) when is_binary(Topic) ->
+    case ets:member(route, Topic) of
+        true  ->
+            try ets:lookup_element(route, Topic, 2) catch error:badarg -> [] end;
+        false ->
+            []
+    end.
 
-%%%=============================================================================
-%%% Internal Functions
-%%%=============================================================================
+%% @doc Add Route.
+-spec add_route(emqttd_topic:topic(), pid()) -> ok.
+add_route(Topic, Pid) when is_pid(Pid) ->
+    call(pick(Topic), {add_route, Topic, Pid}).
 
-insert_routes([], _Pid) ->
+%% @doc Add Routes.
+-spec add_routes(list(emqttd_topic:topic()), pid()) -> ok.
+add_routes([], _Pid) ->
     ok;
-insert_routes(Topics, Pid) ->
-    {Routes, ReverseRoutes} = routes(Topics, Pid),
-    ets:insert(route, Routes),
-    ets:insert(reverse_route, ReverseRoutes).
+add_routes([Topic], Pid) ->
+    add_route(Topic, Pid);
+
+add_routes(Topics, Pid) ->
+    lists:foreach(fun({Router, Slice}) ->
+                call(Router, {add_routes, Slice, Pid})
+        end, slice(Topics)).
+
+%% @doc Delete Route.
+-spec delete_route(emqttd_topic:topic(), pid()) -> ok.
+delete_route(Topic, Pid) ->
+    cast(pick(Topic), {delete_route, Topic, Pid}).
+
+%% @doc Delete Routes.
+-spec delete_routes(list(emqttd_topic:topic()), pid()) -> ok.
+delete_routes([Topic], Pid) ->
+    delete_route(Topic, Pid);
+
+delete_routes(Topics, Pid) ->
+    lists:foreach(fun({Router, Slice}) ->
+                cast(Router, {delete_routes, Slice, Pid})
+        end, slice(Topics)).
+
+%% @private Slice topics.
+slice(Topics) ->
+    dict:to_list(lists:foldl(fun(Topic, Dict) ->
+                    dict:append(pick(Topic), Topic, Dict)
+            end, dict:new(), Topics)).
+
+%% @private Pick a router.
+pick(Topic) ->
+    gproc_pool:pick_worker(router, Topic).
 
-routes(Topics, Pid) ->
-    lists:unzip([{{Topic, Pid}, {Pid, Topic}} || Topic <- Topics]).
+stop(Id) when is_integer(Id) ->
+    gen_server2:call(emqttd:reg_name(?MODULE, Id), stop).
 
-delete_route({Topic, Pid}) ->
-    ets:delete_object(reverse_route, {Pid, Topic}),
-    ets:delete_object(route, {Topic, Pid}).
+call(Router, Request) ->
+    gen_server2:call(Router, Request, infinity).
 
-delete_route_only({Topic, Pid}) ->
-    ets:delete_object(route, {Topic, Pid}).
+cast(Router, Msg) ->
+    gen_server2:cast(Router, Msg).
+
+init([Pool, Id, StatsFun, Opts]) ->
+
+    %% Calls from pubsub should be scheduled first?
+    process_flag(priority, high),
+
+    ?GPROC_POOL(join, Pool, Id),
+
+    emqttd:seed_now(),
+
+    AgingSecs = proplists:get_value(route_aging, Opts, 5),
+
+    %% Aging Timer
+    {ok, AgingTref} = start_tick(AgingSecs + random:uniform(AgingSecs)),
+
+    Aging = #aging{topics = dict:new(), time = AgingSecs, tref = AgingTref},
+
+    {ok, #state{pool = Pool, id = Id, statsfun = StatsFun, aging = Aging}}.
+
+start_tick(Secs) ->
+    timer:send_interval(timer:seconds(Secs), {clean, aged}).
+
+handle_call(stop, _From, State) ->
+    {stop, normal, ok, State};
+
+handle_call({add_route, Topic, Pid}, _From, State) ->
+    ets:insert(route, {Topic, Pid}),
+    {reply, ok, setstats(State)};
+
+handle_call({add_routes, Topics, Pid}, _From, State) ->
+    ets:insert(route, [{Topic, Pid} || Topic <- Topics]),
+    {reply, ok, setstats(State)};
+
+handle_call(Req, _From, State) ->
+   ?UNEXPECTED_REQ(Req, State).
+
+handle_cast({delete_route, Topic, Pid}, State = #state{aging = Aging}) ->
+    ets:delete_object(route, {Topic, Pid}),
+    NewState =
+    case has_route(Topic) of
+        false -> State#state{aging = store_aged(Topic, Aging)};
+        true  -> State
+    end,
+    {noreply, setstats(NewState)};
+
+handle_cast({delete_routes, Topics, Pid}, State) ->
+    NewAging =
+    lists:foldl(fun(Topic, Aging) ->
+                    ets:delete_object(route, {Topic, Pid}),
+                    case has_route(Topic) of
+                        false -> store_aged(Topic, Aging);
+                        true  -> Aging
+                    end
+            end, State#state.aging, Topics),
+    {noreply, setstats(State#state{aging = NewAging})};
+
+handle_cast(Msg, State) ->
+    ?UNEXPECTED_MSG(Msg, State).
+
+handle_info({clean, aged}, State = #state{aging = Aging}) ->
+
+    #aging{topics = Dict, time = Time} = Aging,
+
+    ByTime  = emqttd_util:now_to_secs() - Time,
+
+    Dict1 = try_clean(ByTime, dict:to_list(Dict)),
+
+    NewAging = Aging#aging{topics = dict:from_list(Dict1)},
+
+    {noreply, State#state{aging = NewAging}, hibernate};
+
+handle_info(Info, State) ->
+    ?UNEXPECTED_INFO(Info, State).
+
+terminate(_Reason, #state{pool = Pool, id = Id, aging = #aging{tref = TRef}}) ->
+    timer:cancel(TRef),
+    ?GPROC_POOL(leave, Pool, Id).
+
+code_change(_OldVsn, State, _Extra) ->
+    {ok, State}.
+
+try_clean(ByTime, List) ->
+    try_clean(ByTime, List, []).
+
+try_clean(_ByTime, [], Acc) ->
+    Acc;
+
+try_clean(ByTime, [{Topic, TS} | Left], Acc) ->
+    case has_route(Topic) of
+        false ->
+            try_clean2(ByTime, {Topic, TS}, Left, Acc);
+        true  ->
+            try_clean(ByTime, Left, Acc)
+    end.
+
+try_clean2(ByTime, {Topic, TS}, Left, Acc) when TS > ByTime ->
+    try_clean(ByTime, Left, [{Topic, TS} | Acc]);
+
+try_clean2(ByTime, {Topic, _TS}, Left, Acc) ->
+    TopicR = #mqtt_topic{topic = Topic, node = node()},
+    case mnesia:transaction(fun try_remove_topic/1, [TopicR]) of
+        {atomic, _}      -> ok;
+        {aborted, Error} -> lager:error("Clean Topic '~s' Error: ~p", [Topic, Error])
+    end,
+    try_clean(ByTime, Left, Acc).
+
+try_remove_topic(TopicR = #mqtt_topic{topic = Topic}) ->
+    %% Lock topic first
+    case mnesia:wread({topic, Topic}) of
+        [] ->
+            ok; %% mnesia:abort(not_found);
+        [TopicR] ->
+            %% Remove topic and trie
+            delete_topic(TopicR),
+            emqttd_trie:delete(Topic);
+        _More ->
+            %% Remove topic only
+            delete_topic(TopicR)
+    end.
 
-with_stats(Fun) ->
-    Ok = Fun(), setstats(), Ok.
+delete_topic(TopicR) ->
+    mnesia:delete_object(topic, TopicR, write).
 
-setstats() ->
-    lists:foreach(fun setstat/1, [{route, 'routes/count'},
-                                  {reverse_route, 'routes/reverse'}]).
+store_aged(Topic, Aging = #aging{topics = Dict}) ->
+    Now = emqttd_util:now_to_secs(),
+    Aging#aging{topics = dict:store(Topic, Now, Dict)}.
 
-setstat({Tab, Stat}) ->
-    emqttd_stats:setstat(Stat, ets:info(Tab, size)).
+setstats(State = #state{statsfun = StatsFun}) ->
+    StatsFun(route), State.
 

+ 1 - 1
src/emqttd_serializer.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_session.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_session_sup.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 2 - 5
src/emqttd_sm.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -85,10 +85,7 @@ mnesia(copy) ->
 %%------------------------------------------------------------------------------
 -spec start_link(atom(), pos_integer()) -> {ok, pid()} | ignore | {error, any()}.
 start_link(Pool, Id) ->
-    gen_server2:start_link({local, name(Id)}, ?MODULE, [Pool, Id], []).
-
-name(Id) ->
-    list_to_atom("emqttd_sm_" ++ integer_to_list(Id)).
+    gen_server2:start_link({local, emqttd:reg_name(?MODULE, Id)}, ?MODULE, [Pool, Id], []).
 
 %%------------------------------------------------------------------------------
 %% @doc Start a session

+ 1 - 1
src/emqttd_sm_helper.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_sm_sup.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 2 - 2
src/emqttd_stats.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -143,7 +143,7 @@ setstats(Stat, MaxStat, Val) ->
 %%%=============================================================================
 
 init([]) ->
-    random:seed(os:timestamp()),
+    emqttd:seed_now(),
     ets:new(?STATS_TAB, [set, public, named_table, {write_concurrency, true}]),
     Topics = ?SYSTOP_CLIENTS ++ ?SYSTOP_SESSIONS ++ ?SYSTOP_PUBSUB ++ ?SYSTOP_RETAINED,
     ets:insert(?STATS_TAB, [{Topic, 0} || Topic <- Topics]),

+ 1 - 1
src/emqttd_sup.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_sysmon.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_sysmon_sup.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 11 - 9
src/emqttd_topic.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal
@@ -31,6 +31,8 @@
 
 -export([join/1, feed_var/3, is_queue/1, systop/1]).
 
+-type topic() :: binary().
+
 %-type type()   :: static | dynamic.
 
 -type word()   :: '' | '+' | '#' | binary().
@@ -39,7 +41,7 @@
 
 -type triple() :: {root | binary(), word(), binary()}.
 
--export_type([word/0, triple/0]).
+-export_type([topic/0, word/0, triple/0]).
 
 -define(MAX_TOPIC_LEN, 4096).
 
@@ -47,7 +49,7 @@
 %% @doc Is wildcard topic?
 %% @end
 %%%-----------------------------------------------------------------------------
--spec wildcard(binary()) -> true | false.
+-spec wildcard(topic()) -> true | false.
 wildcard(Topic) when is_binary(Topic) ->
     wildcard(words(Topic));
 wildcard([]) -> 
@@ -64,8 +66,8 @@ wildcard([_H|T]) ->
 %% @end
 %%------------------------------------------------------------------------------
 -spec match(Name, Filter) -> boolean() when
-    Name    :: binary() | words(),
-    Filter  :: binary() | words().
+    Name    :: topic() | words(),
+    Filter  :: topic() | words().
 match(Name, Filter) when is_binary(Name) and is_binary(Filter) ->
     match(words(Name), words(Filter));
 match([], []) ->
@@ -91,7 +93,7 @@ match([], [_H|_T2]) ->
 %% @doc Validate Topic
 %% @end
 %%------------------------------------------------------------------------------
--spec validate({name | filter, binary()}) -> boolean().
+-spec validate({name | filter, topic()}) -> boolean().
 validate({_, <<>>}) ->
     false;
 validate({_, Topic}) when is_binary(Topic) and (size(Topic) > ?MAX_TOPIC_LEN) ->
@@ -129,7 +131,7 @@ validate3(<<_/utf8, Rest/binary>>) ->
 %% @doc Topic to Triples
 %% @end
 %%%-----------------------------------------------------------------------------
--spec triples(binary()) -> list(triple()).
+-spec triples(topic()) -> list(triple()).
 triples(Topic) when is_binary(Topic) ->
     triples(words(Topic), root, []).
 
@@ -154,7 +156,7 @@ bin(B) when is_binary(B) -> B.
 %% @doc Split Topic Path to Words
 %% @end
 %%------------------------------------------------------------------------------
--spec words(binary()) -> words().
+-spec words(topic()) -> words().
 words(Topic) when is_binary(Topic) ->
     [word(W) || W <- binary:split(Topic, <<"/">>, [global])].
 
@@ -167,7 +169,7 @@ word(Bin)     -> Bin.
 %% @doc Queue is a special topic name that starts with "$Q/"
 %% @end
 %%------------------------------------------------------------------------------
--spec is_queue(binary()) -> boolean().
+-spec is_queue(topic()) -> boolean().
 is_queue(<<"$Q/", _Queue/binary>>) ->
     true;
 is_queue(_) ->

+ 1 - 1
src/emqttd_trace.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_trace_sup.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/emqttd_trie.erl

@@ -1,5 +1,5 @@
 %%%-----------------------------------------------------------------------------
-%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
+%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
 %%%
 %%% Permission is hereby granted, free of charge, to any person obtaining a copy
 %%% of this software and associated documentation files (the "Software"), to deal

+ 0 - 0
src/emqttd_util.erl


Некоторые файлы не были показаны из-за большого количества измененных файлов