seapig-server 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/seapig-server +643 -263
- data/lib/seapig-server.rb +1 -2
- data/lib/seapig-server/version.rb +3 -0
- metadata +19 -109
- data/README.rdoc +0 -31
- data/bin/seapig-server-intro +0 -757
- data/lib/seapig/version.rb +0 -3
- data/test/dummy/log/development.log +0 -1665
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/-o-ZYYvENmbyGnbDFt6qAW7obI2QzsO9rM9EA7XlUHg.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/1PVg-zJVu7--eXgp92_OEGf9YMjumXrwhnEbNQdV4h8.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/2R9FCEwuVplMI7I5GoTBtw2Er_ap6H5s2otDK-m5XCk.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/3eEw6ayC9zVKEVm7sgtqdiFFsi-0w4VyeWxR-hk72xg.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/5Lly_CA8DZvPhQV2jDQx-Y6P_y3Ygra9t5jfSlGhHDA.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/66l7LMqenzmlIoboeeCMFEZ6J_6zFdXmqNs-gu79P94.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/6vXjezhMg67rV3BDCPOxLeOVbgCHn0aDwBXCA-N7_To.cache +0 -2
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/7SWrjVR_xhTD9BCS4ifXzZIDn6dp3guSJjF8v5pYDRc.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/98GLSy3KoIvHccw_zbxLg3FpmPEUKmGUr5oL7YZmvOU.cache +0 -127
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/9Kel6H2jL5qJlsGHIcYRbxGaV-rMj_teA3CD1eaUVmk.cache +0 -2
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/9T1WawqT-S8RLLgLI4J-o5mcGyy-wwU2mYMBwTuTl4o.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/BXgyeZH3I-u535wy4F0jP3wmfV8m8WIWtXqHJKdBC2Q.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/BsW5E247kS632hUZ5-NHcjkfprM3AwsB8dksndomUAY.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/CW1ly2CbYhIQ3PoOCrGTUOxS8a03kI-4spp4REHx6mc.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/Duqe6Cf2ThStcjIWL29RZnPilo1v6Vi7p86VOEBKqCs.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/ETKdv5blZ86XWjAQcmxAQq2wK6RT9QvGqd7uV7DK1Iw.cache +0 -2
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/HCc1doOYzZaBpAX7ozNMpo1ErZFli_aaKFQ73oRipH0.cache +0 -2
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/KS-8mb2c29Zj9YWoyTAojF-sqg-aKMYQr6FkxGuoBWQ.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/Lgka3bEq-I8Y6xz-LVIYNVeNIWkmW1NKDnOHOkYcxtE.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/OI6uxGcnsKavdWTtwDAasU3wPx8QXhzBgV0X2n1KjMQ.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/PgVoTat4a0VNolKPJS5RtDX7XcbAGbsqhquIWgWBBWE.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/Tq5PhgpHymowY-e-a3airP7Q2OwxNuNC0792hdlAJRc.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/VegRto_fFjOSBWQRgNRnnOiV3yByrpUI9b5IEhRvrDI.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/VqL7bJxBiq13xYePLO71Spa6RfD5dFCeRRLGYb5jEqw.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/X5QxTUYFP4VOH_7p2Qh34msJtFA6fOnJ0mM7Zip7dRU.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/_2P0GKBCbJ61e8RdTBx4rJr8TsUYKFJYd7N0ZhA7o6k.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/_mxi_K1gl7n32DYq8bFfbWrIRQrU3QQiuukc63_UBb4.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/a1-5JrPXbtVr0ytoeAR0GzDsG_rlYUHm_sKEC1VHrrM.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/aShHx921rUO4rZzH4135LWkt4TSWfqhpQMN8JsV2OqE.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/b9Ac87wpHRTGZL-mBacNdb343KQ1426WdjSu03OVlz8.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/beCZt_nFEyk5iX6v4bgC3qrdFMgSM0IgrwxaBTFEFA0.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/bkclf1HVUxdntd8cKLvWGX5Pq-E12kwCwakqLo8RoN4.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/csrXH9BNqM8JCRjroMFCt2hluEvIvM0neY_ZQySl58A.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/fsxRcZUqoELh6-D0RWowwDKHYmUkGzh5HMFuwMMWk1g.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/h2XCtwcdt21JcAtjhfoOuIjifaMeMaYwPcFGnmNc2ng.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/hZi1k6tpxxCGYxRe7zY74ItcOI8gZrREOpGuA8JSpGg.cache +0 -3
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/j3q-1jQzykD1sMeX9INl7jygCKtC0XJ8JkWkFQkL6qg.cache +0 -2
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/j4VcbkSejSElTiJk3ehlEcJE9HRTG7T14-wVhUDocaA.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/jU_8gMY9eZiN785s1lakKFjnK5ox1EA-Na1fKxuYcjs.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/pEhaat2KBd5SrT7szC_8R1_6hK17FTpvoRFkmCRSD3M.cache +0 -2
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/q3UrpsuPTq0hMrkTWoYoYEZL4u05PdVc4L5NXKuFDgQ.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/sX4-Kn9knBu7plHg7gUT-ryVktGvmZfacNm1cUysjWs.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/umxcUEQeoCOqF0ZwXlNqJEOyT_vhMK8iGO4--jduIDU.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/urzLHu3R3qKeFqygSDL0NVDcyw8BFEA6i9jFeweVZQ4.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/vCljKmhe1Sy9j9TDIB9DrQRa8dYlkPg8nyvsZfqfCmw.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/vn6FA8BrDkisPSH4VFlc7tgmkzpx1DsOtDp5C3cdaRU.cache +0 -1
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/wtuO4JuGsyO8emVQL7t1bm1fvl6YzGfBHc3tJJ49uvs.cache +0 -2
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/z9KVC85iQuroh2hLYTGZhgZHDlh7183qTlfed3Uhtnw.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/znvRewDLwMSRAUjlQozzMBQ_IGWvw9fVVOh9aeaXxTA.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/zxZcbwt4p6Q1Z7yZfuWYKTXjetmNZI8MgkKazd4PjoA.cache +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b3225ad7e10a412888e773d999c3891af4a69d12
|
|
4
|
+
data.tar.gz: a9dba35e923b127d8604245d2e70071eb76dc18e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cfcc0da84581bf7c38e23e5f53ebffba31f3bf8f1fa4ba92d9050643c44f45adca72eeb7b46f3bafd507aabd867de0759b16af4641dd9b3f9e6750df4520ee72
|
|
7
|
+
data.tar.gz: 4b613077e54af4b556a9456d1df492f0dd80cf06eab0b33a13c9963d819db113e59d23fd7e53e2b1bb861dab1db7688c59d9114acd80ed4b4e23e8176a89e394
|
data/bin/seapig-server
CHANGED
|
@@ -6,20 +6,32 @@ require 'oj'
|
|
|
6
6
|
require 'jsondiff'
|
|
7
7
|
require 'hana'
|
|
8
8
|
require 'set'
|
|
9
|
+
require 'slop'
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
OPTIONS = Slop.parse { |o|
|
|
13
|
+
o.string '-l', '--listen', "Address to listen on (default: 127.0.0.1)", default: "127.0.0.1"
|
|
14
|
+
o.int '-p', '--port', "Port to listen on (default: 3001)"
|
|
15
|
+
o.bool '-d', '--debug', 'Show debug messages'
|
|
16
|
+
o.bool '-dm', '--debug-memory', 'Turn on allocation tracing'
|
|
17
|
+
o.bool '-v', '--verbose', 'Show info messages'
|
|
18
|
+
o.on '-h', '--help' do puts o; exit end
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
DEBUG = OPTIONS.debug?
|
|
22
|
+
INFO = (OPTIONS.verbose? or DEBUG)
|
|
23
|
+
HOST = OPTIONS["listen"].split(":")[0]
|
|
24
|
+
PORT = (OPTIONS["port"] or (OPTIONS["listen"].split(':')[1] or "3001").to_i)
|
|
15
25
|
|
|
16
26
|
OBJECT_CACHE_SIZE = 1
|
|
17
27
|
|
|
18
28
|
$stdout.sync = true
|
|
29
|
+
$stderr.sync = true
|
|
19
30
|
|
|
20
31
|
Oj.default_options = { mode: :compat }
|
|
21
32
|
|
|
22
33
|
|
|
34
|
+
|
|
23
35
|
module WebSocket
|
|
24
36
|
module Frame
|
|
25
37
|
class Data < String
|
|
@@ -52,11 +64,14 @@ class String
|
|
|
52
64
|
end
|
|
53
65
|
|
|
54
66
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
if OPTIONS["debug-memory"]
|
|
68
|
+
#require 'memory_profiler'
|
|
69
|
+
#MemoryProfiler.start
|
|
70
|
+
require 'objspace'
|
|
71
|
+
ObjectSpace.trace_object_allocations_start
|
|
72
|
+
$dump_heap = false
|
|
73
|
+
Signal.trap("USR1") { $dump_heap = true }
|
|
74
|
+
end
|
|
60
75
|
|
|
61
76
|
#
|
|
62
77
|
# Code is layered, with each layer only communicating with neighbouring layers (e.g. object store never directly talks to em or sockets).
|
|
@@ -75,68 +90,159 @@ Signal.trap("USR1") {
|
|
|
75
90
|
#
|
|
76
91
|
|
|
77
92
|
|
|
78
|
-
|
|
93
|
+
class SeapigObjectStore
|
|
79
94
|
|
|
80
|
-
|
|
95
|
+
attr_reader :objects_by_id, :consumers, :producers, :connections # only for InternalClient
|
|
96
|
+
attr_reader :internal_client
|
|
81
97
|
|
|
82
|
-
@@producers = {} # {pattern_or_id => {client}}; for assessing spawning possibility
|
|
83
|
-
@@consumers = {} # {pattern_or_id => {client}}; for assessing spawning need, for assessing holding need
|
|
84
98
|
|
|
85
|
-
|
|
86
|
-
|
|
99
|
+
def initialize
|
|
100
|
+
@objects_by_id = {} # {id => object}; stores all existing SeapigObjects
|
|
87
101
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
102
|
+
@connections = {} # {connection_handle => connection}; stores all connections
|
|
103
|
+
@connections_id_seq = 0 # sequence for connection id assignment
|
|
104
|
+
@clients_id_seq = 0 # sequence for client id assignment
|
|
91
105
|
|
|
106
|
+
@producers = {} # {pattern_or_id => {client}}; for assessing spawning possibility
|
|
107
|
+
@consumers = {} # {pattern_or_id => {client}}; for assessing spawning need, for assessing holding need
|
|
92
108
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
109
|
+
@dependents = {} # {id_depended_on => {id_depending}}; for assessing spawning need, for assessing holding need, for assessing reproduction need
|
|
110
|
+
@dependencies = {} # {id_depending => {id_depended_on}}; for updating dependents
|
|
111
|
+
|
|
112
|
+
@queue = [] # [object]; objects in need of production
|
|
113
|
+
@produced = {} # {id_being_produced => {version}}; for assessing enqueuing/dequeuing need
|
|
114
|
+
|
|
115
|
+
@internal_client = InternalClient.new(self)
|
|
116
|
+
@internal_client.connect
|
|
117
|
+
|
|
118
|
+
@internal_client.statistics_entity_register(["connections","count"], last: {retain: true, show: true})
|
|
119
|
+
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def connection_register(connection_handle, details)
|
|
124
|
+
raise "Connection double register" if @connections[connection_handle]
|
|
125
|
+
id = (@connections_id_seq += 1)
|
|
126
|
+
@connections[connection_handle] = SeapigConnection.new(id, connection_handle, details)
|
|
127
|
+
@internal_client.statistics_record(["connections","count"], @connections.size)
|
|
128
|
+
@internal_client.connections_changed
|
|
129
|
+
id
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def connection_unregister(connection_handle)
|
|
134
|
+
raise "Unregister of not registered connection" if not connection = @connections[connection_handle]
|
|
135
|
+
connection.clients.each_value { |client|
|
|
136
|
+
client_unregister(connection, client)
|
|
99
137
|
}
|
|
138
|
+
@connections.delete(connection_handle)
|
|
139
|
+
@internal_client.statistics_record(["connections","count"], @connections.size)
|
|
140
|
+
@internal_client.connections_changed
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def client_register(connection_handle, client_handle, on_object_produce, on_object_update, on_object_destroy)
|
|
145
|
+
raise "Client registration attempted with non-registered connection" if not connection = @connections[connection_handle]
|
|
146
|
+
raise "Double client-register" if connection.clients.has_key?(client_handle)
|
|
147
|
+
id = (@clients_id_seq += 1)
|
|
148
|
+
connection.clients[client_handle] = SeapigClient.new(id, (client_handle == @internal_client), on_object_produce, on_object_update, on_object_destroy)
|
|
149
|
+
@internal_client.connections_changed
|
|
150
|
+
id
|
|
100
151
|
end
|
|
101
152
|
|
|
102
153
|
|
|
103
|
-
def
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
154
|
+
def client_options_set(connection_handle, client_handle, options)
|
|
155
|
+
client = @connections[connection_handle].clients[client_handle]
|
|
156
|
+
client.options = options
|
|
157
|
+
@internal_client.clients_changed(client)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def client_unregister(connection_handle, client_handle)
|
|
162
|
+
raise "Client un-registration attempted with non-registered connection" if not connection = @connections[connection_handle]
|
|
163
|
+
raise "Attempt to unregister client that has not been registered" if not client = connection.clients[client_handle]
|
|
164
|
+
client.produces.each { |pattern| producer_unregister(connection_handle, client_handle, pattern) }
|
|
165
|
+
client.consumes.each { |pattern| consumer_unregister(connection_handle, client_handle, pattern) }
|
|
166
|
+
version_set(connection_handle, client_handle, client.producing[0],nil,nil) if client.producing
|
|
167
|
+
connection.clients.delete(client_handle)
|
|
168
|
+
@internal_client.connections_changed
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def consumer_register(connection_handle, client_handle, pattern_or_id)
|
|
173
|
+
client = @connections[connection_handle].clients[client_handle]
|
|
174
|
+
client.consumes << pattern_or_id
|
|
175
|
+
changed_objects = []
|
|
176
|
+
@consumers[pattern_or_id] = Set.new if not @consumers[pattern_or_id]
|
|
177
|
+
@consumers[pattern_or_id].add(client)
|
|
178
|
+
SeapigObjectStore.matching(pattern_or_id, @producers.merge(@objects_by_id)).each { |matching_id|
|
|
179
|
+
@objects_by_id[matching_id].consumer_register(pattern_or_id, client) if @objects_by_id[matching_id]
|
|
180
|
+
changed_objects << spawn(matching_id).id if not @objects_by_id[matching_id]
|
|
181
|
+
}
|
|
182
|
+
@internal_client.objects_changed(changed_objects) if changed_objects.size > 0
|
|
183
|
+
@internal_client.consumers_changed
|
|
184
|
+
@internal_client.clients_changed(client)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def producer_register(connection_handle, client_handle, pattern_or_id)
|
|
189
|
+
client = @connections[connection_handle].clients[client_handle]
|
|
190
|
+
client.produces << pattern_or_id
|
|
191
|
+
changed_objects = []
|
|
192
|
+
@producers[pattern_or_id] = Set.new if not @producers[pattern_or_id]
|
|
193
|
+
@producers[pattern_or_id].add(client)
|
|
194
|
+
SeapigObjectStore.matching(pattern_or_id, @consumers.merge(@dependents)).each { |matching_id|
|
|
195
|
+
@objects_by_id[matching_id].producer_register(pattern_or_id, client) if @objects_by_id[matching_id]
|
|
196
|
+
changed_objects << spawn(matching_id).id if not @objects_by_id[matching_id]
|
|
109
197
|
}
|
|
110
|
-
|
|
198
|
+
dequeue(client,nil) if not client.producing
|
|
199
|
+
@internal_client.objects_changed(changed_objects) if changed_objects.size > 0
|
|
200
|
+
@internal_client.producers_changed if @internal_client
|
|
201
|
+
@internal_client.clients_changed(client)
|
|
111
202
|
end
|
|
112
203
|
|
|
113
204
|
|
|
114
|
-
def
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
205
|
+
def consumer_unregister(connection_handle, client_handle, pattern_or_id)
|
|
206
|
+
client = @connections[connection_handle].clients[client_handle]
|
|
207
|
+
raise "Consumer unregister without register" if (not @consumers[pattern_or_id]) or (not @consumers[pattern_or_id].include?(client))
|
|
208
|
+
client.consumes.delete(pattern_or_id)
|
|
209
|
+
changed_objects = []
|
|
210
|
+
@consumers[pattern_or_id].delete(client)
|
|
211
|
+
@consumers.delete(pattern_or_id) if @consumers[pattern_or_id].size == 0
|
|
212
|
+
SeapigObjectStore.matching(pattern_or_id,@producers.merge(@objects_by_id)).each { |matching_id|
|
|
213
|
+
@objects_by_id[matching_id].consumer_unregister(pattern_or_id, client) if @objects_by_id[matching_id]
|
|
214
|
+
changed_objects << despawn(@objects_by_id[matching_id]).id if @objects_by_id[matching_id] and (not @objects_by_id[matching_id].alive?) and (not @dependents[matching_id])
|
|
121
215
|
}
|
|
216
|
+
@internal_client.objects_changed(changed_objects) if changed_objects.size > 0
|
|
217
|
+
@internal_client.consumers_changed
|
|
218
|
+
@internal_client.clients_changed(client)
|
|
122
219
|
end
|
|
123
220
|
|
|
124
221
|
|
|
125
|
-
def
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
222
|
+
def producer_unregister(connection_handle, client_handle, pattern_or_id)
|
|
223
|
+
client = @connections[connection_handle].clients[client_handle]
|
|
224
|
+
raise "Producer unregister without register" if (not @producers[pattern_or_id]) or (not @producers[pattern_or_id].include?(client))
|
|
225
|
+
changed_objects = []
|
|
226
|
+
@producers[pattern_or_id].delete(client)
|
|
227
|
+
@producers.delete(pattern_or_id) if @producers[pattern_or_id].size == 0
|
|
228
|
+
client.produces.delete(pattern_or_id)
|
|
229
|
+
SeapigObjectStore.matching(pattern_or_id,@consumers.merge(@dependents)).each { |matching_id|
|
|
230
|
+
@objects_by_id[matching_id].producer_unregister(pattern_or_id, client) if @objects_by_id[matching_id]
|
|
231
|
+
changed_objects << despawn(@objects_by_id[matching_id]).id if @objects_by_id[matching_id] and (not @objects_by_id[matching_id].alive?) and (not @dependents[matching_id])
|
|
132
232
|
}
|
|
233
|
+
if client.producing and (pattern_or_id.starexp? ? (client.producing[0] =~ pattern_or_id.starexp) : (client.producing[0] == pattern_or_id)) #FIXME: overlaping production patterns are not supported
|
|
234
|
+
version_set(connection_handle, client_handle, client.producing[0], nil,nil)
|
|
235
|
+
end
|
|
236
|
+
@internal_client.objects_changed(changed_objects) if changed_objects.size > 0
|
|
237
|
+
@internal_client.producers_changed if @internal_client
|
|
238
|
+
@internal_client.clients_changed(client)
|
|
133
239
|
end
|
|
134
240
|
|
|
135
241
|
|
|
136
|
-
def
|
|
242
|
+
def version_get(connection_handle, client_handle, id, version)
|
|
137
243
|
raise "version_get called on starexp, that doesn't make sense" if id.starexp?
|
|
138
|
-
return [0,{}] if not
|
|
139
|
-
|
|
244
|
+
return [0,{}] if not @objects_by_id.has_key?(id)
|
|
245
|
+
@objects_by_id[id].version_get(version)
|
|
140
246
|
end
|
|
141
247
|
|
|
142
248
|
|
|
@@ -145,76 +251,148 @@ module SeapigObjectStore
|
|
|
145
251
|
# - false => given version has no data (aka. stall)
|
|
146
252
|
# - true => given version exists (data unknown)
|
|
147
253
|
# - nil => given version could not be generated (data unknown)
|
|
148
|
-
def
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
254
|
+
def version_set(connection_handle, client_handle, id, version, data)
|
|
255
|
+
|
|
256
|
+
client = @connections[connection_handle].clients[client_handle]
|
|
257
|
+
changed_objects = []
|
|
258
|
+
|
|
259
|
+
if client.producing and id == client.producing[0]
|
|
260
|
+
raise "requested_version (%s) not in @produced[id] (%s)"%[client.producing[1].inspect,@produced[id].inspect] if not @produced[id].include?(client.producing[1])
|
|
261
|
+
@produced[id].delete(client.producing[1])
|
|
262
|
+
@produced.delete(id) if @produced[id].size == 0
|
|
263
|
+
client.producing = nil
|
|
264
|
+
was_producing = true
|
|
265
|
+
else
|
|
266
|
+
was_producing = false
|
|
157
267
|
end
|
|
158
268
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
next if not (dependent = @@objects_by_id[dependent_id])
|
|
167
|
-
if dependent.version_needed and dependent.version_needed[id] and version.kind_of?(Integer) and dependent.version_needed[id].kind_of?(Integer) and dependent.version_needed[id] < version
|
|
168
|
-
dependent.version_needed[id] = version
|
|
169
|
-
enqueue(dependent)
|
|
170
|
-
end
|
|
171
|
-
}
|
|
269
|
+
object = @objects_by_id[id]
|
|
270
|
+
if (object or (not data.nil?)) and (@objects_by_id.has_key?(id) or @dependents[id] or @consumers.keys.find { |pattern| id =~ pattern.starexp })
|
|
271
|
+
changed_objects << (object = spawn(id)).id if not object
|
|
272
|
+
provided_version_highest_known, provided_version_highest_inferred = object.version_set(data, version)
|
|
273
|
+
if provided_version_highest_inferred
|
|
274
|
+
puts "Received version is highest inferred" if DEBUG
|
|
275
|
+
changed_objects << object.id if not changed_objects.include?(object.id)
|
|
172
276
|
if version.kind_of? Hash
|
|
173
|
-
object.
|
|
174
|
-
old_dependencies = (
|
|
175
|
-
new_dependencies = (
|
|
277
|
+
object.version_highest_inferred = {} if not object.version_highest_inferred.kind_of?(Hash)
|
|
278
|
+
old_dependencies = (@dependencies[id] or Set.new)
|
|
279
|
+
new_dependencies = (@dependencies[id] = Set.new(version.keys))
|
|
176
280
|
(new_dependencies - old_dependencies).each { |added_dependency|
|
|
177
|
-
|
|
178
|
-
|
|
281
|
+
added_dependency_version_highest_inferred = @objects_by_id[added_dependency] ? @objects_by_id[added_dependency].version_highest_inferred : nil
|
|
282
|
+
object.version_highest_inferred[added_dependency] = SeapigObject.version_newer(added_dependency_version_highest_inferred, version[added_dependency])
|
|
283
|
+
changed_objects.concat dependent_add(added_dependency, object.id)
|
|
179
284
|
}
|
|
180
285
|
(old_dependencies & new_dependencies).each { |kept_dependency|
|
|
181
|
-
|
|
286
|
+
kept_dependency_version_highest_inferred = @objects_by_id[kept_dependency] ? @objects_by_id[kept_dependency].version_highest_inferred : nil
|
|
287
|
+
object.version_highest_inferred[kept_dependency] = SeapigObject.version_newer(kept_dependency_version_highest_inferred, version[kept_dependency])
|
|
182
288
|
}
|
|
183
289
|
(old_dependencies - new_dependencies).each { |removed_dependency|
|
|
184
|
-
object.
|
|
185
|
-
dependent_remove(removed_dependency, object.id)
|
|
290
|
+
object.version_highest_inferred.delete(removed_dependency)
|
|
291
|
+
changed_objects.concat dependent_remove(removed_dependency, object.id)
|
|
186
292
|
}
|
|
187
293
|
else
|
|
188
|
-
object.
|
|
294
|
+
object.version_highest_inferred = version
|
|
189
295
|
end
|
|
190
296
|
end
|
|
297
|
+
if provided_version_highest_known
|
|
298
|
+
puts "Received version is highest known" if DEBUG
|
|
299
|
+
changed_objects << object.id if not changed_objects.include?(object.id)
|
|
300
|
+
(@dependents[id] or Set.new).each { |dependent_id|
|
|
301
|
+
raise if not @objects_by_id.has_key?(dependent_id)
|
|
302
|
+
next if not (dependent = @objects_by_id[dependent_id])
|
|
303
|
+
dependent.version_highest_inferred[id] = version if SeapigObject.version_newer?(dependent.version_highest_inferred[id],version)
|
|
304
|
+
changed_objects << enqueue(dependent)
|
|
305
|
+
}
|
|
306
|
+
end
|
|
191
307
|
enqueue(object)
|
|
192
308
|
end
|
|
193
309
|
|
|
194
|
-
|
|
310
|
+
if was_producing and not client.producing
|
|
311
|
+
dequeue(client,nil)
|
|
312
|
+
@internal_client.clients_changed(client)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
changed_objects.compact!
|
|
316
|
+
@internal_client.objects_changed(changed_objects) if changed_objects.size > 0
|
|
195
317
|
end
|
|
196
318
|
|
|
197
319
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
320
|
+
#TODO: add locking
|
|
321
|
+
def cache_get(connection_handle, client_handle, object_id, key)
|
|
322
|
+
return nil if not @objects_by_id.has_key?(object_id)
|
|
323
|
+
@objects_by_id[object_id].cache_get(key)
|
|
201
324
|
end
|
|
202
325
|
|
|
203
326
|
|
|
204
|
-
def
|
|
205
|
-
return value if not
|
|
206
|
-
|
|
327
|
+
def cache_set(cannection_handle, client_handle, object_id, key, value)
|
|
328
|
+
return value if not @objects_by_id.has_key?(object_id)
|
|
329
|
+
@objects_by_id[object_id].cache_set(key, value)
|
|
207
330
|
value
|
|
208
331
|
end
|
|
209
332
|
|
|
210
333
|
|
|
334
|
+
def pp
|
|
335
|
+
[
|
|
336
|
+
"Objects:", @objects_by_id.values.map { |object| " %s"%[object.inspect] },
|
|
337
|
+
"Queue:", @queue.map { |object| " %s"%[object.inspect] },
|
|
338
|
+
"Connections:", @connections.map { |_,connection| " %3s - %s"%[connection.id, connection.clients.map { |_,client| client.pretty_id }.join(", ")] },
|
|
339
|
+
"Clients:", @connections.map { |_,connection| connection.clients.map { |_,client|
|
|
340
|
+
" %-22s produces: %s consumes: %s%s"%[client.pretty_id, client.produces.to_a.inspect, client.consumes.to_a.inspect,
|
|
341
|
+
(client.producing and (" producing: "+client.producing.inspect) or "")]
|
|
342
|
+
} }.flatten,
|
|
343
|
+
"Produced:", @produced.map { |object,versions| " %s - %s"%[object,versions.inspect] }
|
|
344
|
+
].flatten.select { |str| str.size > 0 }.join("\n")+"\n"
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
|
|
211
348
|
private
|
|
212
349
|
|
|
350
|
+
class SeapigConnection
|
|
351
|
+
|
|
352
|
+
attr_reader :id, :clients, :connection_handle, :details
|
|
353
|
+
|
|
354
|
+
def initialize(id, connection_handle, details)
|
|
355
|
+
@id = id
|
|
356
|
+
@connection_handle = connection_handle
|
|
357
|
+
@details = details
|
|
358
|
+
@clients = {}
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
class SeapigClient
|
|
365
|
+
|
|
366
|
+
attr_reader :consumes, :produces, :internal
|
|
367
|
+
attr_accessor :options, :producing
|
|
368
|
+
|
|
369
|
+
def initialize(id, internal, on_object_produce, on_object_update, on_object_destroy)
|
|
370
|
+
@id = id
|
|
371
|
+
@on_object_produce = on_object_produce
|
|
372
|
+
@on_object_update = on_object_update
|
|
373
|
+
@on_object_destroy = on_object_destroy
|
|
374
|
+
@options = {}
|
|
375
|
+
@consumes = Set.new
|
|
376
|
+
@produces = Set.new
|
|
377
|
+
@producing = nil
|
|
378
|
+
@internal = internal
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def object_produce(*args); @on_object_produce.call(*args); end
|
|
382
|
+
def object_update(*args); @on_object_update.call(*args); end
|
|
383
|
+
def object_destroy(*args); @on_object_destroy.call(*args); end
|
|
384
|
+
|
|
385
|
+
def pretty_id
|
|
386
|
+
(@options["name"] or "")+":"+@id.to_s
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
end
|
|
390
|
+
|
|
213
391
|
|
|
214
392
|
class SeapigObject
|
|
215
393
|
|
|
216
|
-
attr_reader :id, :versions, :direct_producers, :wildcard_producers
|
|
217
|
-
attr_accessor :
|
|
394
|
+
attr_reader :id, :versions, :direct_producers, :direct_consumers, :wildcard_producers, :wildcard_consumers
|
|
395
|
+
attr_accessor :version_highest_inferred, :state
|
|
218
396
|
|
|
219
397
|
def initialize(id)
|
|
220
398
|
@id = id
|
|
@@ -223,8 +401,9 @@ private
|
|
|
223
401
|
@wildcard_consumers = {}
|
|
224
402
|
@direct_producers = Set.new
|
|
225
403
|
@wildcard_producers = {}
|
|
226
|
-
@
|
|
404
|
+
@version_highest_inferred = nil
|
|
227
405
|
@cache = []
|
|
406
|
+
@state = nil
|
|
228
407
|
end
|
|
229
408
|
|
|
230
409
|
|
|
@@ -232,6 +411,9 @@ private
|
|
|
232
411
|
@wildcard_consumers.keys.each { |client|
|
|
233
412
|
client.object_destroy(@id)
|
|
234
413
|
}
|
|
414
|
+
(Set.new(@wildcard_producers.keys)+@direct_producers).each { |client|
|
|
415
|
+
client.object_destroy(@id)
|
|
416
|
+
}
|
|
235
417
|
end
|
|
236
418
|
|
|
237
419
|
|
|
@@ -240,40 +422,50 @@ private
|
|
|
240
422
|
end
|
|
241
423
|
|
|
242
424
|
|
|
243
|
-
def version_set(data,version
|
|
244
|
-
return false if data == nil
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
discard_below
|
|
425
|
+
def version_set(data,version)
|
|
426
|
+
return [false, false] if data == nil
|
|
427
|
+
provided_version_highest_known = if (data.kind_of?(Hash) or data == false) and SeapigObject.version_newer?(version_highest_known, version)
|
|
428
|
+
@versions << [SeapigObject.version_clone(version),data]
|
|
429
|
+
(Set.new(@wildcard_consumers.keys)+@direct_consumers).each { |client| client.object_update(@id, version, data) } if data
|
|
430
|
+
versions_with_valid_data = 0
|
|
431
|
+
discard_below = @versions.size - 1
|
|
432
|
+
while discard_below > 0 and versions_with_valid_data < 1
|
|
433
|
+
versions_with_valid_data += 1 if @versions[discard_below][1]
|
|
434
|
+
discard_below -= 1
|
|
435
|
+
end
|
|
436
|
+
discard_below.times { @versions.shift }
|
|
437
|
+
true
|
|
255
438
|
end
|
|
256
|
-
|
|
257
|
-
|
|
439
|
+
provided_version_highest_inferred = SeapigObject.version_newer?(@version_highest_inferred, version)
|
|
440
|
+
[provided_version_highest_known, provided_version_highest_inferred]
|
|
258
441
|
end
|
|
259
442
|
|
|
260
443
|
|
|
261
|
-
def
|
|
444
|
+
def self.version_clone(version)
|
|
445
|
+
return nil if version == nil
|
|
446
|
+
return version if version.kind_of?(Fixnum) or version.kind_of?(Float)
|
|
447
|
+
version.clone
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def version_highest_known
|
|
262
452
|
return nil if not @versions[-1]
|
|
263
453
|
@versions[-1][0]
|
|
264
454
|
end
|
|
265
455
|
|
|
266
456
|
|
|
267
457
|
def self.version_newer?(latest,vb)
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
458
|
+
latest_type = latest.kind_of?(Hash) ? 3 : latest.kind_of?(Array) ? 2 : latest.nil? ? 0 : 1
|
|
459
|
+
vb_type = vb.kind_of?(Hash) ? 3 : vb.kind_of?(Array) ? 2 : vb.nil? ? 0 : 1
|
|
460
|
+
return latest_type < vb_type if latest_type != vb_type
|
|
461
|
+
if latest.kind_of?(Hash)
|
|
462
|
+
(latest.keys & vb.keys).each { |key|
|
|
463
|
+
return true if version_newer?(latest[key], vb[key])
|
|
464
|
+
}
|
|
465
|
+
return vb.size < latest.size #THINK: is this the right way to go...
|
|
466
|
+
else
|
|
467
|
+
return (latest <=> vb) == -1
|
|
468
|
+
end
|
|
277
469
|
end
|
|
278
470
|
|
|
279
471
|
|
|
@@ -346,7 +538,7 @@ private
|
|
|
346
538
|
|
|
347
539
|
|
|
348
540
|
def inspect
|
|
349
|
-
'<SO:%s:%s:%s:%s:%s:%s:%s>'%[@id, @versions
|
|
541
|
+
'<SO:%s:%s:%s:%s:%s:%s:%s>'%[@id, @versions[-1][0].inspect,@direct_producers.map(&:pretty_id).inspect,@wildcard_producers.keys.map(&:pretty_id).inspect,@direct_consumers.map(&:pretty_id).inspect,@wildcard_consumers.keys.map(&:pretty_id).inspect,@version_highest_inferred.inspect]
|
|
350
542
|
end
|
|
351
543
|
|
|
352
544
|
end
|
|
@@ -365,96 +557,280 @@ private
|
|
|
365
557
|
end
|
|
366
558
|
|
|
367
559
|
|
|
368
|
-
def
|
|
560
|
+
def spawn(id)
|
|
369
561
|
puts "Creating:\n "+id if DEBUG
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
562
|
+
@objects_by_id[id] = object = SeapigObject.new(id)
|
|
563
|
+
@producers.each_pair.map { |pattern,clients| clients.each { |client| object.producer_register(pattern,client) if pattern.starexp? and (id =~ pattern.starexp) or (id == pattern) } }
|
|
564
|
+
@consumers.each_pair.map { |pattern,clients| clients.each { |client| object.consumer_register(pattern,client) if pattern.starexp? and (id =~ pattern.starexp) or (id == pattern) } }
|
|
373
565
|
enqueue(object)
|
|
374
566
|
object
|
|
375
567
|
end
|
|
376
568
|
|
|
377
569
|
|
|
378
|
-
def
|
|
570
|
+
def despawn(object)
|
|
379
571
|
puts "Deleting:\n "+object.id if DEBUG
|
|
380
|
-
raise "Despawning object that should stay alive" if object.alive? or
|
|
572
|
+
raise "Despawning object that should stay alive" if object.alive? or @dependents[object.id]
|
|
381
573
|
object.destroy
|
|
382
|
-
(
|
|
574
|
+
(@dependencies.delete(object.id) or []).each { |dependency_id|
|
|
383
575
|
dependent_remove(dependency_id, object.id)
|
|
384
576
|
}
|
|
385
|
-
|
|
577
|
+
@objects_by_id.delete(object.id)
|
|
386
578
|
end
|
|
387
579
|
|
|
388
580
|
|
|
389
|
-
def
|
|
390
|
-
|
|
391
|
-
|
|
581
|
+
def enqueue(object)
|
|
582
|
+
puts "Enqueuing: "+object.id if DEBUG
|
|
583
|
+
if object.version_highest_inferred and object.version_highest_known == object.version_highest_inferred
|
|
584
|
+
object.state = { current: true } if object.version_highest_known == object.version_highest_inferred
|
|
585
|
+
@queue.delete(object)
|
|
586
|
+
puts " No need." if DEBUG
|
|
587
|
+
nil
|
|
392
588
|
else
|
|
393
|
-
return if
|
|
394
|
-
|
|
589
|
+
return nil if @queue.include?(object) or (@produced[object.id] and @produced[object.id].include?(object.version_highest_inferred))
|
|
590
|
+
if object.version_highest_inferred.kind_of?(Hash) and object.version_highest_inferred.find { |dependency, dependency_version|
|
|
591
|
+
(not dependency_version == 0) and
|
|
592
|
+
((not @objects_by_id[dependency]) or
|
|
593
|
+
SeapigObject.version_newer?(@objects_by_id[dependency].version_highest_known, dependency_version))
|
|
594
|
+
}
|
|
595
|
+
object.state = { current: false, producing: false, enqueued: false }
|
|
596
|
+
return object
|
|
597
|
+
end
|
|
598
|
+
object.state = { current: false, producing: false, enqueued: true }
|
|
599
|
+
@queue << object
|
|
395
600
|
(Set.new(object.direct_producers) + object.wildcard_producers.keys).find { |client|
|
|
396
|
-
dequeue(client, object) if not
|
|
601
|
+
dequeue(client, object) if not client.producing
|
|
397
602
|
}
|
|
603
|
+
object
|
|
398
604
|
end
|
|
399
605
|
end
|
|
400
606
|
|
|
401
607
|
|
|
402
|
-
def
|
|
403
|
-
object =
|
|
404
|
-
return false if not
|
|
405
|
-
version_snapshot =
|
|
608
|
+
def dequeue(client,object)
|
|
609
|
+
object = @queue.find { |candidate_object| candidate_object.direct_producers.include?(client) or candidate_object.wildcard_producers.has_key?(client) } if not object
|
|
610
|
+
return false if not @queue.include?(object)
|
|
611
|
+
version_snapshot = SeapigObject.version_clone(object.version_highest_inferred)
|
|
612
|
+
@queue.delete(object)
|
|
613
|
+
client.producing = [object.id, version_snapshot]
|
|
614
|
+
(@produced[object.id] ||= Set.new) << version_snapshot
|
|
615
|
+
puts "Dequeuing: "+object.id+" to: "+client.pretty_id+" expected version: "+object.version_highest_inferred.inspect if DEBUG
|
|
616
|
+
object.state = { current: false, producing: true }
|
|
617
|
+
@internal_client.clients_changed(client)
|
|
406
618
|
client.object_produce(object.id, version_snapshot)
|
|
407
|
-
|
|
408
|
-
@@producing[client] = object
|
|
409
|
-
(@@produced[object.id] ||= Set.new) << version_snapshot
|
|
619
|
+
object
|
|
410
620
|
end
|
|
411
621
|
|
|
412
622
|
|
|
413
|
-
def
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
623
|
+
def dependent_add(id, dependent)
|
|
624
|
+
changed_objects = []
|
|
625
|
+
@dependents[id] = Set.new if not @dependents[id]
|
|
626
|
+
@dependents[id] << dependent
|
|
627
|
+
SeapigObjectStore.matching(id, @producers).each { |matching_id|
|
|
628
|
+
changed_objects << spawn(matching_id) if not @objects_by_id[matching_id]
|
|
418
629
|
}
|
|
630
|
+
changed_objects
|
|
419
631
|
end
|
|
420
632
|
|
|
421
633
|
|
|
422
|
-
def
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
634
|
+
def dependent_remove(id, dependent)
|
|
635
|
+
changed_objects = []
|
|
636
|
+
@dependents[id].delete(dependent)
|
|
637
|
+
@dependents.delete(id) if @dependents[id].size == 0
|
|
638
|
+
changed_objects << despawn(@objects_by_id[id]).id if @objects_by_id.include?(id) and (not @objects_by_id[id].alive?) and (not @dependents[id])
|
|
639
|
+
changed_objects
|
|
426
640
|
end
|
|
427
641
|
|
|
428
642
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
class InternalClient
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
def id
|
|
651
|
+
"InternalClient"
|
|
436
652
|
end
|
|
437
653
|
|
|
438
654
|
|
|
655
|
+
def initialize(seapig_object_store)
|
|
656
|
+
@seapig_object_store = seapig_object_store
|
|
657
|
+
@objects_version = [(ENV["SEAPIG_SERVER_SESSION"] or (Time.new.to_f*1000)).to_i,0]
|
|
658
|
+
@consumers_version = @objects_version.clone
|
|
659
|
+
@producers_version = @objects_version.clone
|
|
660
|
+
@connections_version = @objects_version.clone
|
|
661
|
+
@statistics_version = @objects_version.clone
|
|
662
|
+
@statistics_timeslot = nil
|
|
663
|
+
@statistics = {
|
|
664
|
+
seconds: { seconds: 1, keep: 60*5, timestamp: nil, entities: {}},
|
|
665
|
+
minutes: { seconds: 60, keep: 60*5, timestamp: nil, entities: {}},
|
|
666
|
+
hours: { seconds: 60*60, keep: 24*10, timestamp: nil, entities: {}},
|
|
667
|
+
days: { seconds: 24*60*60, keep: 365*1, timestamp: nil, entities: {}}
|
|
668
|
+
}
|
|
669
|
+
@connected = false
|
|
670
|
+
end
|
|
439
671
|
|
|
440
672
|
|
|
673
|
+
def connect
|
|
674
|
+
@seapig_object_store.connection_register(self, {})
|
|
675
|
+
@seapig_object_store.client_register(self, self, method(:object_produce), Proc.new { raise "object_update called on internal client" }, Proc.new { })
|
|
676
|
+
@seapig_object_store.client_options_set(self, self, "name"=>"SeapigInternalClient")
|
|
677
|
+
@seapig_object_store.producer_register(self, self, "SeapigServer::Objects")
|
|
678
|
+
@seapig_object_store.producer_register(self, self, "SeapigServer::Consumers")
|
|
679
|
+
@seapig_object_store.producer_register(self, self, "SeapigServer::Producers")
|
|
680
|
+
@seapig_object_store.producer_register(self, self, "SeapigServer::Connections")
|
|
681
|
+
@seapig_object_store.producer_register(self, self, "SeapigServer::Statistics")
|
|
682
|
+
@connected = true
|
|
683
|
+
end
|
|
441
684
|
|
|
442
685
|
|
|
443
|
-
|
|
686
|
+
def object_produce(object_id, object_version)
|
|
687
|
+
case object_id
|
|
688
|
+
when "SeapigServer::Objects"
|
|
689
|
+
objects = @seapig_object_store.objects_by_id.values.map { |obj|
|
|
690
|
+
[ obj.id, {
|
|
691
|
+
"id"=> obj.id,
|
|
692
|
+
"state"=> (obj.id == object_id ? { current: true} : Oj.load(Oj.dump(obj.state))),
|
|
693
|
+
"version_highest_known"=> obj.version_highest_known,
|
|
694
|
+
"version_highest_inferred"=> Oj.load(Oj.dump(obj.version_highest_inferred)), # do we need to clone here?
|
|
695
|
+
"consumers"=> (Set.new(obj.wildcard_consumers.keys)+obj.direct_consumers).map { |client| client.pretty_id },
|
|
696
|
+
"producers"=> (Set.new(obj.wildcard_producers.keys)+obj.direct_producers).map { |client| client.pretty_id }
|
|
697
|
+
} ]
|
|
698
|
+
}.to_h
|
|
699
|
+
@seapig_object_store.version_set(self, self, "SeapigServer::Objects", @objects_version.clone, objects)
|
|
700
|
+
when "SeapigServer::Consumers"
|
|
701
|
+
consumers = @seapig_object_store.consumers.map { |pattern,clients|
|
|
702
|
+
[pattern, clients.map { |client| client.pretty_id } ]
|
|
703
|
+
}.to_h
|
|
704
|
+
@seapig_object_store.version_set(self, self, "SeapigServer::Consumers", @consumers_version.clone, consumers)
|
|
705
|
+
when "SeapigServer::Producers"
|
|
706
|
+
producers = @seapig_object_store.producers.map { |pattern,clients|
|
|
707
|
+
[pattern, clients.map { |client| client.pretty_id } ]
|
|
708
|
+
}.to_h
|
|
709
|
+
@seapig_object_store.version_set(self, self, "SeapigServer::Producers", @producers_version.clone, producers)
|
|
710
|
+
when "SeapigServer::Connections"
|
|
711
|
+
connections = @seapig_object_store.connections.map { |connection_handle, connection|
|
|
712
|
+
[connection.id, {
|
|
713
|
+
clients: connection.clients.values.map { |client| [client.pretty_id, {
|
|
714
|
+
id: client.pretty_id,
|
|
715
|
+
options: client.options,
|
|
716
|
+
produces: client.produces.to_a,
|
|
717
|
+
consumes: client.consumes.to_a,
|
|
718
|
+
producing: (client.internal ? nil : client.producing)}]}.to_h,
|
|
719
|
+
details: connection.details
|
|
720
|
+
} ]
|
|
721
|
+
}.to_h
|
|
722
|
+
@seapig_object_store.version_set(self, self, "SeapigServer::Connections", @connections_version.clone, connections)
|
|
723
|
+
when "SeapigServer::Statistics"
|
|
724
|
+
statistics = @statistics.map { |scale_name, scale|
|
|
725
|
+
[
|
|
726
|
+
scale_name,
|
|
727
|
+
scale.merge(entities: scale[:entities].map { |entity, data|
|
|
728
|
+
[ entity.join("-"), data.map { |key, value| [key, value.kind_of?(Array) ? value[0...-1] : value] }.to_h ]
|
|
729
|
+
}.to_h )
|
|
730
|
+
] }.to_h
|
|
731
|
+
@seapig_object_store.version_set(self, self, "SeapigServer::Statistics", @statistics_version.clone, statistics)
|
|
732
|
+
end
|
|
733
|
+
end
|
|
444
734
|
|
|
445
735
|
|
|
736
|
+
def objects_changed(object_ids)
|
|
737
|
+
return if object_ids.size == 1 and object_ids[0] == "SeapigServer::Objects"
|
|
738
|
+
@objects_version[1] += 1
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def consumers_changed
|
|
743
|
+
@consumers_version[1] += 1
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
def producers_changed
|
|
748
|
+
@producers_version[1] += 1
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
def clients_changed(client)
|
|
753
|
+
return if client.internal
|
|
754
|
+
connections_changed
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
def connections_changed
|
|
759
|
+
return if not @connected
|
|
760
|
+
@connections_version[1] += 1
|
|
761
|
+
end
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
def statistics_entity_register(entity, metrics)
|
|
765
|
+
@statistics.map { |name,scale|
|
|
766
|
+
statistics_bump_metrics(scale, @statistics[name][:entities][entity] = { metrics: metrics }.merge(metrics.map { |metric, properites| [metric, []] }.to_h))
|
|
767
|
+
}
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
def statistics_bump_metrics(scale, data)
|
|
771
|
+
{ count: 0, sum: 0, average: nil, last: nil, maximum: nil, minimum: nil, histogram: [] }.each { |metric, initial_value|
|
|
772
|
+
next if not data[metric]
|
|
773
|
+
data[metric] << initial_value
|
|
774
|
+
data[metric].shift if data[metric].size > scale[:keep]+1
|
|
775
|
+
}
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
def statistics_timeslot_create
|
|
780
|
+
@statistics_timeslot = Time.new.to_i
|
|
781
|
+
@statistics.each { |name, scale|
|
|
782
|
+
while (next_slot = (scale[:timestamp] or (@statistics_timeslot - scale[:seconds] - 1)) + scale[:seconds]) < @statistics_timeslot
|
|
783
|
+
scale[:timestamp] = next_slot
|
|
784
|
+
scale[:entities].each { |entity, data|
|
|
785
|
+
statistics_bump_metrics(scale, data)
|
|
786
|
+
data[:metrics].each { |metric, properties|
|
|
787
|
+
statistics_record(entity, data[metric][-2], name) if properties[:retain] and data[metric][-2]
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
end
|
|
791
|
+
}
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
def statistics_record(entity, quantity, only_scale = nil)
|
|
796
|
+
statistics_timeslot_create if @statistics_timeslot != Time.new.to_i and not only_scale
|
|
797
|
+
@statistics.each { |name, scale|
|
|
798
|
+
next if only_scale and name != only_scale
|
|
799
|
+
next if not data = scale[:entities][entity]
|
|
800
|
+
data[:last][-1] = quantity if data[:metrics][:last]
|
|
801
|
+
data[:count][-1] += 1 if data[:metrics][:count]
|
|
802
|
+
data[:sum][-1] += quantity if data[:metrics][:sum]
|
|
803
|
+
data[:average][-1] = data[:sum][-1]/data[:count][-1] if data[:metrics][:average]
|
|
804
|
+
data[:minimum][-1] = quantity if data[:metrics][:minimum] and ((not data[:minimum][-1]) or quantity < data[:minimum][-1])
|
|
805
|
+
data[:maximum][-1] = quantity if data[:metrics][:maximum] and ((not data[:maximum][-1]) or quantity > data[:maximum][-1])
|
|
806
|
+
if data[:metrics][:histogram]
|
|
807
|
+
bucket = (Math.log(quantity,10)*data[:metrics][:histogram][:multiplier]).floor
|
|
808
|
+
data[:histogram][-1].push 0 while data[:histogram][-1].size < (bucket+1)
|
|
809
|
+
data[:histogram][-1][bucket] += 1
|
|
810
|
+
end
|
|
811
|
+
}
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
def bump
|
|
816
|
+
@statistics_version[1] += 1
|
|
817
|
+
@seapig_object_store.version_set(self, self, "SeapigServer::Statistics", @statistics_version.clone, true)
|
|
818
|
+
@seapig_object_store.version_set(self, self, "SeapigServer::Objects", @objects_version.clone, true)
|
|
819
|
+
@seapig_object_store.version_set(self, self, "SeapigServer::Consumers", @consumers_version.clone, true)
|
|
820
|
+
@seapig_object_store.version_set(self, self, "SeapigServer::Producers", @producers_version.clone, true)
|
|
821
|
+
@seapig_object_store.version_set(self, self, "SeapigServer::Connections", @connections_version.clone, true)
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
end
|
|
446
825
|
|
|
447
|
-
#TODO:
|
|
448
|
-
# * Refactor to have ClientSpace class/module with Clients inside
|
|
449
826
|
|
|
450
827
|
|
|
451
|
-
class
|
|
828
|
+
class SeapigWebsocketClient
|
|
452
829
|
|
|
453
830
|
attr_reader :produces, :consumes, :socket, :producing, :index, :pong_time
|
|
454
831
|
attr_accessor :options
|
|
455
832
|
|
|
456
833
|
@@clients_by_socket = {}
|
|
457
|
-
@@count = 0
|
|
458
834
|
|
|
459
835
|
|
|
460
836
|
def self.[](socket)
|
|
@@ -462,77 +838,68 @@ class Client
|
|
|
462
838
|
end
|
|
463
839
|
|
|
464
840
|
|
|
465
|
-
def initialize(socket)
|
|
466
|
-
@
|
|
467
|
-
puts "Client connected:\n "+@index.to_s if DEBUG
|
|
841
|
+
def initialize(seapig_object_store, socket)
|
|
842
|
+
@seapig_object_store = seapig_object_store
|
|
468
843
|
@socket = socket
|
|
469
844
|
@options = {}
|
|
470
|
-
@produces = Set.new
|
|
471
|
-
@consumes = Set.new
|
|
472
845
|
@versions = {}
|
|
473
|
-
@
|
|
846
|
+
@consumes = Set.new
|
|
474
847
|
@@clients_by_socket[socket] = self
|
|
475
848
|
self.pong
|
|
849
|
+
@seapig_object_store.connection_register(self, {})
|
|
850
|
+
@index = @seapig_object_store.client_register(self, self, method(:object_produce), method(:object_update), method(:object_destroy))
|
|
851
|
+
puts "Client connected:\n "+@index.to_s if DEBUG
|
|
476
852
|
end
|
|
477
853
|
|
|
478
854
|
|
|
479
|
-
def
|
|
480
|
-
|
|
855
|
+
def options_set(options)
|
|
856
|
+
@options = options
|
|
857
|
+
@seapig_object_store.client_options_set(self, self, options)
|
|
481
858
|
end
|
|
482
859
|
|
|
483
860
|
|
|
484
|
-
def
|
|
485
|
-
|
|
861
|
+
def id #duplicate
|
|
862
|
+
(@options['name'] or "") + ':' + @index.to_s
|
|
486
863
|
end
|
|
487
864
|
|
|
488
865
|
|
|
489
866
|
def destroy
|
|
490
867
|
puts "Client disconnected:\n "+@index.to_s if DEBUG
|
|
491
868
|
@@clients_by_socket.delete(@socket)
|
|
492
|
-
@
|
|
493
|
-
@
|
|
494
|
-
producing = @producing
|
|
495
|
-
@producing = nil
|
|
496
|
-
SeapigObjectStore.version_set(self,producing[0],nil,nil,producing[1]) if producing
|
|
869
|
+
@seapig_object_store.client_unregister(self, self)
|
|
870
|
+
@seapig_object_store.connection_unregister(self)
|
|
497
871
|
end
|
|
498
872
|
|
|
499
873
|
|
|
500
874
|
def producer_register(pattern, known_version)
|
|
501
|
-
@
|
|
502
|
-
|
|
503
|
-
SeapigObjectStore.version_set(self, pattern, known_version, true, false) if known_version and not pattern.starexp?
|
|
875
|
+
@seapig_object_store.producer_register(self, self, pattern)
|
|
876
|
+
@seapig_object_store.version_set(self, self, pattern, known_version, true) if known_version and not pattern.starexp?
|
|
504
877
|
end
|
|
505
878
|
|
|
506
879
|
|
|
507
880
|
def producer_unregister(pattern)
|
|
508
|
-
@
|
|
509
|
-
SeapigObjectStore.producer_unregister(pattern, self)
|
|
510
|
-
if @producing and (pattern.starexp? ? (@producing[0] =~ pattern.starexp) : (@producing[0] == pattern)) #NOTE: overlaping production patterns are not supported
|
|
511
|
-
producing = @producing
|
|
512
|
-
@producing = nil
|
|
513
|
-
SeapigObjectStore.version_set(self,producing[0],nil,nil,producing[1])
|
|
514
|
-
end
|
|
881
|
+
@seapig_object_store.producer_unregister(self, self, pattern)
|
|
515
882
|
end
|
|
516
883
|
|
|
517
884
|
|
|
518
885
|
def consumer_register(pattern, known_version)
|
|
519
886
|
@consumes.add(pattern)
|
|
520
887
|
@versions[pattern] = known_version if not pattern.starexp?
|
|
521
|
-
|
|
888
|
+
@seapig_object_store.consumer_register(self, self, pattern)
|
|
522
889
|
gc_versions
|
|
523
890
|
end
|
|
524
891
|
|
|
525
892
|
|
|
526
893
|
def consumer_unregister(pattern)
|
|
527
894
|
@consumes.delete(pattern)
|
|
528
|
-
|
|
895
|
+
@seapig_object_store.consumer_unregister(self, self, pattern)
|
|
529
896
|
gc_versions
|
|
530
897
|
end
|
|
531
898
|
|
|
532
899
|
|
|
533
900
|
def gc_versions
|
|
534
901
|
@versions.keys.each { |object_id|
|
|
535
|
-
@versions.delete(object_id) if not
|
|
902
|
+
@versions.delete(object_id) if not @consumes.find { |pattern|
|
|
536
903
|
pattern.starexp? and (object_id =~ pattern.starexp) or (pattern == object_id)
|
|
537
904
|
}
|
|
538
905
|
}
|
|
@@ -542,44 +909,40 @@ class Client
|
|
|
542
909
|
def object_update(object_id, object_version, object_data)
|
|
543
910
|
#THINK: should we propagate stalls to clients?
|
|
544
911
|
return if object_version == 0 or object_version == @versions[object_id]
|
|
545
|
-
old_version, old_data =
|
|
912
|
+
old_version, old_data = @seapig_object_store.version_get(self, self, object_id, (@versions[object_id] or 0))
|
|
546
913
|
data = if old_version == 0
|
|
547
914
|
{ "value" => object_data }
|
|
548
915
|
else
|
|
549
|
-
diff =
|
|
550
|
-
diff =
|
|
916
|
+
diff = @seapig_object_store.cache_get(self, self, object_id,[:diff,old_version,object_version])
|
|
917
|
+
diff = @seapig_object_store.cache_set(self, self, object_id,[:diff,old_version,object_version],JsonDiff.generate(old_data, object_data)) if not diff
|
|
551
918
|
{ "patch" => diff }
|
|
552
919
|
end
|
|
553
920
|
|
|
554
921
|
json = Oj.dump({
|
|
555
922
|
"action" => 'object-update',
|
|
556
923
|
"id" => object_id,
|
|
557
|
-
"
|
|
558
|
-
"
|
|
924
|
+
"version-old" => old_version,
|
|
925
|
+
"version-new" => object_version,
|
|
559
926
|
}.merge(data))
|
|
560
927
|
puts "Sending:\n %8iB %s to %s"%[json.size, object_id, id] if DEBUG
|
|
561
928
|
@versions[object_id] = object_version
|
|
562
929
|
@socket.send json
|
|
930
|
+
@seapig_object_store.internal_client.statistics_record(["message","outgoing","count"], 1)
|
|
931
|
+
@seapig_object_store.internal_client.statistics_record(["message","outgoing","size"], json.size)
|
|
563
932
|
end
|
|
564
933
|
|
|
565
934
|
|
|
566
935
|
def object_destroy(object_id)
|
|
936
|
+
@versions.delete(object_id) #TODO: regression test, sub-unsub-sub
|
|
567
937
|
@socket.send Oj.dump("action" => 'object-destroy', "id" => object_id)
|
|
568
938
|
end
|
|
569
939
|
|
|
570
940
|
|
|
571
941
|
def object_patch(object_id, patch, value, from_version, to_version)
|
|
572
942
|
raise "patching wildcard object. no." if object_id.starexp?
|
|
573
|
-
requested_object_id, requested_version = @producing
|
|
574
|
-
if requested_object_id == object_id
|
|
575
|
-
@producing = nil
|
|
576
|
-
else
|
|
577
|
-
requested_version = false
|
|
578
|
-
end
|
|
579
|
-
new_version = to_version
|
|
580
943
|
|
|
581
944
|
new_data = if patch
|
|
582
|
-
object_version, object_data =
|
|
945
|
+
object_version, object_data = @seapig_object_store.version_get(self,self,object_id,from_version)
|
|
583
946
|
print "Patching:\n version: "+object_version.inspect+"\n from_version: "+from_version.inspect+"\n to_version: "+to_version.inspect+"\n patch_size: "+(patch and patch.size.to_s or "nil")+"\n --> " if DEBUG
|
|
584
947
|
if from_version == object_version
|
|
585
948
|
puts 'clean' if DEBUG
|
|
@@ -602,17 +965,15 @@ class Client
|
|
|
602
965
|
nil
|
|
603
966
|
end
|
|
604
967
|
|
|
605
|
-
|
|
968
|
+
@seapig_object_store.version_set(self,self,object_id,to_version,new_data)
|
|
606
969
|
end
|
|
607
970
|
|
|
608
971
|
|
|
609
972
|
def object_produce(object_id, object_version)
|
|
610
973
|
raise "Can't produce a wildcard object" if object_id.starexp?
|
|
611
974
|
raise "Client already producing something (producing: %s, trying to assign: %s)"%[@producing.inspect, [object_id,object_version].inspect] if @producing
|
|
612
|
-
raise "Can't produce that pattern: "+@produces.inspect+" "+object_id.inspect if not @produces.find { |pattern| object_id =~ pattern.starexp }
|
|
613
975
|
puts "Assigning:\n "+object_id+':'+object_version.inspect+' to: '+self.id if DEBUG
|
|
614
|
-
@socket.send Oj.dump("action" => 'object-produce', "id" => object_id, "version"=>object_version)
|
|
615
|
-
@producing = [object_id, object_version]
|
|
976
|
+
@socket.send Oj.dump("action" => 'object-produce', "id" => object_id, "version-inferred"=>object_version)
|
|
616
977
|
end
|
|
617
978
|
|
|
618
979
|
|
|
@@ -635,14 +996,9 @@ class Client
|
|
|
635
996
|
@@clients_by_socket.each_pair { |socket,client| socket.close if Time.new - client.pong_time > 60 }
|
|
636
997
|
end
|
|
637
998
|
|
|
638
|
-
|
|
639
|
-
def self.pp
|
|
640
|
-
"Clients:\n"+@@clients_by_socket.values.map { |client| " %-20s produces:%s consumes:%s"%[client.id,client.produces.to_a,client.consumes.to_a] }.join("\n")+"\n"
|
|
641
|
-
end
|
|
642
999
|
end
|
|
643
1000
|
|
|
644
1001
|
|
|
645
|
-
|
|
646
1002
|
#TODO:
|
|
647
1003
|
# * change protocol to use "pattern" instead of "id"
|
|
648
1004
|
# * change "object-patch" to something nicer
|
|
@@ -650,91 +1006,115 @@ end
|
|
|
650
1006
|
processing_times = []
|
|
651
1007
|
processing_times_sum = 0
|
|
652
1008
|
|
|
1009
|
+
seapig_object_store = SeapigObjectStore.new
|
|
1010
|
+
|
|
1011
|
+
seapig_object_store.internal_client.statistics_entity_register(["message","incoming","count"], count: {show: true})
|
|
1012
|
+
seapig_object_store.internal_client.statistics_entity_register(["message","incoming","size"], count: {show: false}, sum: {show: true}, average: {show: true}, maximum: {show: true}, histogram: {show: true, multiplier: 10})
|
|
1013
|
+
seapig_object_store.internal_client.statistics_entity_register(["message","outgoing","count"], count: {show: true})
|
|
1014
|
+
seapig_object_store.internal_client.statistics_entity_register(["message","outgoing","size"], count: {show: false}, sum: {show: true}, average: {show: true}, maximum: {show: true}, histogram: {show: true, multiplier: 10})
|
|
1015
|
+
seapig_object_store.internal_client.statistics_entity_register(["message","incoming","processing-time"], count: {show: false}, sum: {show: true}, average: {show: true}, maximum: {show: true}, histogram: {show: true, multiplier: 10})
|
|
1016
|
+
|
|
1017
|
+
|
|
1018
|
+
def safety_net
|
|
1019
|
+
Proc.new { |*args|
|
|
1020
|
+
begin
|
|
1021
|
+
yield *args
|
|
1022
|
+
rescue =>e
|
|
1023
|
+
puts "*"*70+" EXCEPTION"
|
|
1024
|
+
p e
|
|
1025
|
+
e.backtrace.each { |line| puts line }
|
|
1026
|
+
raise
|
|
1027
|
+
end
|
|
1028
|
+
}
|
|
1029
|
+
end
|
|
1030
|
+
|
|
1031
|
+
|
|
653
1032
|
EM.run {
|
|
654
1033
|
|
|
655
1034
|
|
|
656
1035
|
WebSocket::EventMachine::Server.start(host: HOST, port: PORT) { |client_socket|
|
|
657
1036
|
|
|
658
|
-
client_socket.onmessage { |
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
puts Client.pp
|
|
693
|
-
puts SeapigObjectStore.pp
|
|
694
|
-
puts "Processing:\n time: %.3fs\n count: %i\n average: %.3fs\n total: %.3fs"%[processing_times[-1], processing_times.size, processing_times_sum / processing_times.size, processing_times_sum]
|
|
695
|
-
end
|
|
696
|
-
puts "message:%3i t:%.3fs Σt:%.3fs t̅:%.3fs"%[processing_times.size, processing_times[-1], processing_times_sum, processing_times_sum / processing_times.size,] if INFO and not DEBUG
|
|
697
|
-
rescue => e
|
|
698
|
-
puts "Message processing error:\n "
|
|
699
|
-
p e
|
|
700
|
-
e.backtrace.each { |line| puts line }
|
|
701
|
-
raise
|
|
1037
|
+
client_socket.onmessage &safety_net { |message_json|
|
|
1038
|
+
started_at = Time.new
|
|
1039
|
+
client = SeapigWebsocketClient[client_socket]
|
|
1040
|
+
message = Oj.load message_json
|
|
1041
|
+
puts "-"*80 + ' ' + Time.new.to_s if DEBUG
|
|
1042
|
+
print "Message:\n from: %-20s\n action: %-30s\n param: %-50s "%[client.id, message['action'], Oj.dump(message.select { |k,v| ['pattern','id','options'].include?(k) })] if DEBUG
|
|
1043
|
+
puts if DEBUG
|
|
1044
|
+
case message['action']
|
|
1045
|
+
when 'object-producer-register'
|
|
1046
|
+
next unless message['pattern']
|
|
1047
|
+
client.producer_register(message['pattern'], message['version-known'])
|
|
1048
|
+
when 'object-producer-unregister'
|
|
1049
|
+
next unless message['pattern']
|
|
1050
|
+
client.producer_unregister(message['pattern'])
|
|
1051
|
+
when 'object-patch'
|
|
1052
|
+
next unless message['id']
|
|
1053
|
+
client.object_patch(message['id'], message['patch'], message['value'], message['version-old'], message['version-new'])
|
|
1054
|
+
when 'object-consumer-register'
|
|
1055
|
+
next unless message['pattern']
|
|
1056
|
+
client.consumer_register(message['pattern'], message['version-known'])
|
|
1057
|
+
when 'object-consumer-unregister'
|
|
1058
|
+
next unless message['pattern']
|
|
1059
|
+
client.consumer_unregister(message['pattern'])
|
|
1060
|
+
when 'client-options-set'
|
|
1061
|
+
next unless message['options']
|
|
1062
|
+
client.options_set(message['options'])
|
|
1063
|
+
else
|
|
1064
|
+
raise 'WTF, got message with action: ' + message['action'].inspect
|
|
1065
|
+
end
|
|
1066
|
+
processing_times << (Time.new.to_f - started_at.to_f)
|
|
1067
|
+
processing_times_sum += processing_times[-1]
|
|
1068
|
+
if DEBUG
|
|
1069
|
+
puts seapig_object_store.pp
|
|
1070
|
+
puts "Processing:\n time: %.3fs\n count: %i\n average: %.3fs\n total: %.3fs"%[processing_times[-1], processing_times.size, processing_times_sum / processing_times.size, processing_times_sum]
|
|
702
1071
|
end
|
|
1072
|
+
seapig_object_store.internal_client.statistics_record(["message","incoming","count"], 1)
|
|
1073
|
+
seapig_object_store.internal_client.statistics_record(["message","incoming","size"], message_json.size)
|
|
1074
|
+
seapig_object_store.internal_client.statistics_record(["message","incoming","processing-time"], (processing_times[-1]*1000000).floor)
|
|
1075
|
+
puts "message:%3i t:%.3fs Σt:%.3fs t̅:%.3fs"%[processing_times.size, processing_times[-1], processing_times_sum, processing_times_sum / processing_times.size,] if INFO and not DEBUG
|
|
703
1076
|
}
|
|
704
1077
|
|
|
705
1078
|
|
|
706
|
-
client_socket.onopen {
|
|
707
|
-
client_socket.onclose {
|
|
708
|
-
client_socket.onpong {
|
|
1079
|
+
client_socket.onopen &safety_net { SeapigWebsocketClient.new(seapig_object_store, client_socket) }
|
|
1080
|
+
client_socket.onclose &safety_net { SeapigWebsocketClient[client_socket].destroy if SeapigWebsocketClient[client_socket] }
|
|
1081
|
+
client_socket.onpong &safety_net { SeapigWebsocketClient[client_socket].pong }
|
|
709
1082
|
}
|
|
710
1083
|
|
|
711
1084
|
puts "Listening on %s:%s"%[HOST,PORT] if INFO or DEBUG
|
|
712
|
-
|
|
1085
|
+
Socket.open(:UNIX, :DGRAM) { |s| s.connect(Socket.pack_sockaddr_un(ENV['NOTIFY_SOCKET'])); s.sendmsg "READY=1" } if ENV['NOTIFY_SOCKET']
|
|
713
1086
|
|
|
714
|
-
EM.add_periodic_timer(10
|
|
715
|
-
EM.add_periodic_timer(10
|
|
716
|
-
EM.add_periodic_timer(10
|
|
1087
|
+
EM.add_periodic_timer(10, &safety_net { SeapigWebsocketClient.send_pings })
|
|
1088
|
+
EM.add_periodic_timer(10, &safety_net { SeapigWebsocketClient.send_heartbeats })
|
|
1089
|
+
EM.add_periodic_timer(10, &safety_net { SeapigWebsocketClient.check_ping_timeouts })
|
|
717
1090
|
|
|
718
|
-
EM.add_periodic_timer(1
|
|
1091
|
+
EM.add_periodic_timer(1, &safety_net {
|
|
719
1092
|
now = Time.new
|
|
720
1093
|
puts "CPU time used: %7.3f%%"%[(processing_times_sum-$last_processing_times_sum)*100.0/(now - $last_cpu_time)] if $last_cpu_time and DEBUG
|
|
721
1094
|
$last_cpu_time = now
|
|
722
1095
|
$last_processing_times_sum = processing_times_sum
|
|
723
|
-
|
|
1096
|
+
seapig_object_store.internal_client.bump
|
|
1097
|
+
if $dump_heap
|
|
1098
|
+
$dump_heap = false
|
|
1099
|
+
GC.start
|
|
1100
|
+
#MemoryProfiler.stop.pretty_print(to_file: "memory_"+Time.new.strftime("%Y%m%d%H%M%S"))
|
|
1101
|
+
#MemoryProfiler.start
|
|
1102
|
+
open("heap_dump_"+Time.new.strftime("%Y%m%d%H%M%S"),"w") { |file| ObjectSpace.dump_all(output: file) }
|
|
1103
|
+
end
|
|
1104
|
+
})
|
|
724
1105
|
|
|
725
1106
|
|
|
726
1107
|
close_reader, close_writer = IO.pipe
|
|
727
1108
|
|
|
728
|
-
EM.watch(close_reader
|
|
1109
|
+
EM.watch(close_reader, &safety_net { |connection|
|
|
729
1110
|
connection.notify_readable = true
|
|
730
1111
|
connection.define_singleton_method(:notify_readable) do
|
|
731
1112
|
puts "Shutting down" if INFO or DEBUG
|
|
732
1113
|
exit
|
|
733
1114
|
end
|
|
734
|
-
}
|
|
1115
|
+
})
|
|
735
1116
|
|
|
736
|
-
Signal.trap("INT") {
|
|
737
|
-
puts "SIGINT received, scheduling exit." if DEBUG
|
|
1117
|
+
Signal.trap("INT") { #NOTE: don't puts to std here, it may deadlock
|
|
738
1118
|
close_writer.write('.')
|
|
739
1119
|
}
|
|
740
1120
|
|