hat-event 0.9.27__cp310.cp311.cp312.cp313-abi3-win_amd64.whl
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.
- hat/event/__init__.py +1 -0
- hat/event/adminer/__init__.py +18 -0
- hat/event/adminer/client.py +124 -0
- hat/event/adminer/common.py +27 -0
- hat/event/adminer/server.py +111 -0
- hat/event/backends/__init__.py +0 -0
- hat/event/backends/dummy.py +49 -0
- hat/event/backends/lmdb/__init__.py +9 -0
- hat/event/backends/lmdb/backend.py +319 -0
- hat/event/backends/lmdb/common.py +277 -0
- hat/event/backends/lmdb/conditions.py +102 -0
- hat/event/backends/lmdb/convert/__init__.py +0 -0
- hat/event/backends/lmdb/convert/__main__.py +8 -0
- hat/event/backends/lmdb/convert/convert_v06_to_v07.py +213 -0
- hat/event/backends/lmdb/convert/convert_v07_to_v09.py +175 -0
- hat/event/backends/lmdb/convert/main.py +88 -0
- hat/event/backends/lmdb/convert/v06.py +216 -0
- hat/event/backends/lmdb/convert/v07.py +508 -0
- hat/event/backends/lmdb/convert/v09.py +50 -0
- hat/event/backends/lmdb/convert/version.py +63 -0
- hat/event/backends/lmdb/environment.py +100 -0
- hat/event/backends/lmdb/latestdb.py +116 -0
- hat/event/backends/lmdb/manager/__init__.py +0 -0
- hat/event/backends/lmdb/manager/__main__.py +8 -0
- hat/event/backends/lmdb/manager/common.py +45 -0
- hat/event/backends/lmdb/manager/copy.py +92 -0
- hat/event/backends/lmdb/manager/main.py +34 -0
- hat/event/backends/lmdb/manager/query.py +215 -0
- hat/event/backends/lmdb/refdb.py +234 -0
- hat/event/backends/lmdb/systemdb.py +102 -0
- hat/event/backends/lmdb/timeseriesdb.py +486 -0
- hat/event/backends/memory.py +178 -0
- hat/event/common/__init__.py +144 -0
- hat/event/common/backend.py +91 -0
- hat/event/common/collection/__init__.py +8 -0
- hat/event/common/collection/common.py +28 -0
- hat/event/common/collection/list.py +19 -0
- hat/event/common/collection/tree.py +62 -0
- hat/event/common/common.py +176 -0
- hat/event/common/encoder.py +305 -0
- hat/event/common/json_schema_repo.json +1 -0
- hat/event/common/matches.py +44 -0
- hat/event/common/module.py +142 -0
- hat/event/common/sbs_repo.json +1 -0
- hat/event/common/subscription/__init__.py +22 -0
- hat/event/common/subscription/_csubscription.abi3.pyd +0 -0
- hat/event/common/subscription/common.py +145 -0
- hat/event/common/subscription/csubscription.py +47 -0
- hat/event/common/subscription/pysubscription.py +97 -0
- hat/event/component.py +284 -0
- hat/event/eventer/__init__.py +28 -0
- hat/event/eventer/client.py +260 -0
- hat/event/eventer/common.py +27 -0
- hat/event/eventer/server.py +286 -0
- hat/event/manager/__init__.py +0 -0
- hat/event/manager/__main__.py +8 -0
- hat/event/manager/common.py +48 -0
- hat/event/manager/main.py +387 -0
- hat/event/server/__init__.py +0 -0
- hat/event/server/__main__.py +8 -0
- hat/event/server/adminer_server.py +43 -0
- hat/event/server/engine.py +216 -0
- hat/event/server/engine_runner.py +127 -0
- hat/event/server/eventer_client.py +205 -0
- hat/event/server/eventer_client_runner.py +152 -0
- hat/event/server/eventer_server.py +119 -0
- hat/event/server/main.py +84 -0
- hat/event/server/main_runner.py +212 -0
- hat_event-0.9.27.dist-info/LICENSE +202 -0
- hat_event-0.9.27.dist-info/METADATA +108 -0
- hat_event-0.9.27.dist-info/RECORD +73 -0
- hat_event-0.9.27.dist-info/WHEEL +7 -0
- hat_event-0.9.27.dist-info/entry_points.txt +5 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import collections
|
|
2
|
+
import itertools
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
import lmdb
|
|
6
|
+
|
|
7
|
+
from hat import util
|
|
8
|
+
|
|
9
|
+
from hat.event.backends.lmdb import common
|
|
10
|
+
from hat.event.backends.lmdb import environment
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# depending on dict order for added refs
|
|
14
|
+
class ServerChanges(typing.NamedTuple):
|
|
15
|
+
added: dict[common.EventId,
|
|
16
|
+
tuple[common.Event,
|
|
17
|
+
set[common.EventRef]]]
|
|
18
|
+
removed: dict[common.EventId,
|
|
19
|
+
set[common.EventRef]]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Changes: typing.TypeAlias = dict[common.ServerId, ServerChanges]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class RefDb:
|
|
26
|
+
|
|
27
|
+
def __init__(self,
|
|
28
|
+
env: environment.Environment,
|
|
29
|
+
max_results: int = 4096):
|
|
30
|
+
self._env = env
|
|
31
|
+
self._max_results = max_results
|
|
32
|
+
self._last_event_ids = {}
|
|
33
|
+
self._changes = collections.defaultdict(_create_server_changes)
|
|
34
|
+
|
|
35
|
+
def add(self,
|
|
36
|
+
event: common.Event,
|
|
37
|
+
refs: typing.Iterable[common.EventRef]):
|
|
38
|
+
last_event_id = self._last_event_ids.get(event.id.server)
|
|
39
|
+
if last_event_id and last_event_id > event.id:
|
|
40
|
+
raise Exception('event older than last')
|
|
41
|
+
|
|
42
|
+
self._last_event_ids[event.id.server] = event.id
|
|
43
|
+
|
|
44
|
+
server_changes = self._changes[event.id.server]
|
|
45
|
+
server_changes.added[event.id] = event, set(refs)
|
|
46
|
+
|
|
47
|
+
def remove(self,
|
|
48
|
+
event_id: common.EventId,
|
|
49
|
+
ref: common.EventRef):
|
|
50
|
+
server_changes = self._changes[event_id.server]
|
|
51
|
+
added = server_changes.added.get(event_id)
|
|
52
|
+
|
|
53
|
+
if added and ref in added[1]:
|
|
54
|
+
added[1].remove(ref)
|
|
55
|
+
|
|
56
|
+
if not added[1]:
|
|
57
|
+
server_changes.added.pop(event_id)
|
|
58
|
+
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
removed = server_changes.removed[event_id]
|
|
62
|
+
removed.add(ref)
|
|
63
|
+
|
|
64
|
+
async def query(self,
|
|
65
|
+
params: common.QueryServerParams
|
|
66
|
+
) -> common.QueryResult:
|
|
67
|
+
if (params.last_event_id and
|
|
68
|
+
params.last_event_id.server != params.server_id):
|
|
69
|
+
raise ValueError('invalid server id')
|
|
70
|
+
|
|
71
|
+
max_results = (params.max_results
|
|
72
|
+
if params.max_results is not None and
|
|
73
|
+
params.max_results < self._max_results
|
|
74
|
+
else self._max_results)
|
|
75
|
+
|
|
76
|
+
changes = self._changes
|
|
77
|
+
events = await self._env.execute(_ext_query_events, self._env,
|
|
78
|
+
params.server_id, max_results + 1,
|
|
79
|
+
params.last_event_id)
|
|
80
|
+
|
|
81
|
+
if not params.persisted and len(events) <= max_results:
|
|
82
|
+
last_event_id = events[-1].id if events else params.last_event_id
|
|
83
|
+
changes_max_result = max_results + 1 - len(events)
|
|
84
|
+
changes_events = _query_changes(changes, params.server_id,
|
|
85
|
+
changes_max_result, last_event_id)
|
|
86
|
+
|
|
87
|
+
events.extend(changes_events)
|
|
88
|
+
|
|
89
|
+
if len(events) > max_results:
|
|
90
|
+
events = list(itertools.islice(events, max_results))
|
|
91
|
+
more_follows = True
|
|
92
|
+
|
|
93
|
+
else:
|
|
94
|
+
events = list(events)
|
|
95
|
+
more_follows = False
|
|
96
|
+
|
|
97
|
+
return common.QueryResult(events=events,
|
|
98
|
+
more_follows=more_follows)
|
|
99
|
+
|
|
100
|
+
def create_changes(self) -> Changes:
|
|
101
|
+
self._changes, changes = (
|
|
102
|
+
collections.defaultdict(_create_server_changes), self._changes)
|
|
103
|
+
return changes
|
|
104
|
+
|
|
105
|
+
def ext_write(self,
|
|
106
|
+
txn: lmdb.Transaction,
|
|
107
|
+
changes: Changes):
|
|
108
|
+
db_def = common.db_defs[common.DbType.REF]
|
|
109
|
+
|
|
110
|
+
with self._env.ext_cursor(txn, common.DbType.REF) as cursor:
|
|
111
|
+
for server_changes in changes.values():
|
|
112
|
+
event_ids = {*server_changes.added.keys(),
|
|
113
|
+
*server_changes.removed.keys()}
|
|
114
|
+
|
|
115
|
+
for event_id in event_ids:
|
|
116
|
+
encoded_key = db_def.encode_key(event_id)
|
|
117
|
+
encoded_value = cursor.pop(encoded_key)
|
|
118
|
+
value = (db_def.decode_value(encoded_value)
|
|
119
|
+
if encoded_value else set())
|
|
120
|
+
|
|
121
|
+
added = server_changes.added.get(event_id)
|
|
122
|
+
if added:
|
|
123
|
+
value.update(added[1])
|
|
124
|
+
|
|
125
|
+
removed = server_changes.removed.get(event_id)
|
|
126
|
+
if removed:
|
|
127
|
+
value.difference_update(removed)
|
|
128
|
+
|
|
129
|
+
if not value:
|
|
130
|
+
continue
|
|
131
|
+
|
|
132
|
+
encoded_value = db_def.encode_value(value)
|
|
133
|
+
cursor.put(encoded_key, encoded_value)
|
|
134
|
+
|
|
135
|
+
def ext_cleanup(self,
|
|
136
|
+
txn: lmdb.Transaction,
|
|
137
|
+
refs: typing.Iterable[tuple[common.EventId,
|
|
138
|
+
common.EventRef]]):
|
|
139
|
+
db_def = common.db_defs[common.DbType.REF]
|
|
140
|
+
|
|
141
|
+
with self._env.ext_cursor(txn, common.DbType.REF) as cursor:
|
|
142
|
+
for event_id, ref in refs:
|
|
143
|
+
encoded_key = db_def.encode_key(event_id)
|
|
144
|
+
encoded_value = cursor.pop(encoded_key)
|
|
145
|
+
if not encoded_value:
|
|
146
|
+
continue
|
|
147
|
+
|
|
148
|
+
value = db_def.decode_value(encoded_value)
|
|
149
|
+
value.discard(ref)
|
|
150
|
+
|
|
151
|
+
if not value:
|
|
152
|
+
continue
|
|
153
|
+
|
|
154
|
+
encoded_value = db_def.encode_value(value)
|
|
155
|
+
cursor.put(encoded_key, encoded_value)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _query_changes(changes, server_id, max_results, last_event_id):
|
|
159
|
+
server_changes = changes.get(server_id)
|
|
160
|
+
if not server_changes:
|
|
161
|
+
return []
|
|
162
|
+
|
|
163
|
+
events = (event for _, (event, __) in server_changes.added.items())
|
|
164
|
+
|
|
165
|
+
if last_event_id:
|
|
166
|
+
events = itertools.dropwhile(lambda i: i.id <= last_event_id,
|
|
167
|
+
events)
|
|
168
|
+
|
|
169
|
+
return itertools.islice(events, max_results)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _ext_query_events(env, server_id, max_results, last_event_id):
|
|
173
|
+
db_def = common.db_defs[common.DbType.REF]
|
|
174
|
+
|
|
175
|
+
start_key = (last_event_id if last_event_id
|
|
176
|
+
else common.EventId(server=server_id,
|
|
177
|
+
session=0,
|
|
178
|
+
instance=0))
|
|
179
|
+
stop_key = common.EventId(server=server_id + 1,
|
|
180
|
+
session=0,
|
|
181
|
+
instance=0)
|
|
182
|
+
|
|
183
|
+
encoded_start_key = db_def.encode_key(start_key)
|
|
184
|
+
encoded_stop_key = db_def.encode_key(stop_key)
|
|
185
|
+
|
|
186
|
+
events = collections.deque()
|
|
187
|
+
|
|
188
|
+
with env.ext_begin() as txn:
|
|
189
|
+
with env.ext_cursor(txn, common.DbType.REF) as cursor:
|
|
190
|
+
available = cursor.set_range(encoded_start_key)
|
|
191
|
+
if available and bytes(cursor.key()) == encoded_start_key:
|
|
192
|
+
available = cursor.next()
|
|
193
|
+
|
|
194
|
+
while (available and
|
|
195
|
+
len(events) < max_results and
|
|
196
|
+
bytes(cursor.key()) < encoded_stop_key):
|
|
197
|
+
value = db_def.decode_value(cursor.value())
|
|
198
|
+
ref = util.first(value)
|
|
199
|
+
if not ref:
|
|
200
|
+
continue
|
|
201
|
+
|
|
202
|
+
event = _ext_get_event(env, txn, ref)
|
|
203
|
+
if event:
|
|
204
|
+
# TODO decode key and check event_id == event.id
|
|
205
|
+
events.append(event)
|
|
206
|
+
|
|
207
|
+
available = cursor.next()
|
|
208
|
+
|
|
209
|
+
return events
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _ext_get_event(env, txn, ref):
|
|
213
|
+
if isinstance(ref, common.LatestEventRef):
|
|
214
|
+
db_type = common.DbType.LATEST_DATA
|
|
215
|
+
|
|
216
|
+
elif isinstance(ref, common.TimeseriesEventRef):
|
|
217
|
+
db_type = common.DbType.TIMESERIES_DATA
|
|
218
|
+
|
|
219
|
+
else:
|
|
220
|
+
raise ValueError('unsupported event reference type')
|
|
221
|
+
|
|
222
|
+
db_def = common.db_defs[db_type]
|
|
223
|
+
encoded_key = db_def.encode_key(ref.key)
|
|
224
|
+
|
|
225
|
+
with env.ext_cursor(txn, db_type) as cursor:
|
|
226
|
+
encoded_value = cursor.get(encoded_key)
|
|
227
|
+
if not encoded_value:
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
return db_def.decode_value(encoded_value)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _create_server_changes():
|
|
234
|
+
return ServerChanges({}, collections.defaultdict(set))
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
import lmdb
|
|
4
|
+
|
|
5
|
+
from hat.event.backends.lmdb import common
|
|
6
|
+
from hat.event.backends.lmdb import environment
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Changes(typing.NamedTuple):
|
|
10
|
+
last_event_ids: dict[common.ServerId, common.EventId]
|
|
11
|
+
last_timestamps: dict[common.ServerId, common.Timestamp]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def ext_create(env: environment.Environment,
|
|
15
|
+
txn: lmdb.Transaction,
|
|
16
|
+
version: str,
|
|
17
|
+
identifier: str | None
|
|
18
|
+
) -> 'SystemDb':
|
|
19
|
+
db = SystemDb()
|
|
20
|
+
db._env = env
|
|
21
|
+
db._changes = Changes({}, {})
|
|
22
|
+
|
|
23
|
+
_ext_validate_settings(env, txn, version, identifier)
|
|
24
|
+
|
|
25
|
+
db._last_event_ids = dict(
|
|
26
|
+
env.ext_read(txn, common.DbType.SYSTEM_LAST_EVENT_ID))
|
|
27
|
+
|
|
28
|
+
db._last_timestamps = dict(
|
|
29
|
+
env.ext_read(txn, common.DbType.SYSTEM_LAST_TIMESTAMP))
|
|
30
|
+
|
|
31
|
+
return db
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class SystemDb:
|
|
35
|
+
|
|
36
|
+
def get_last_event_id(self,
|
|
37
|
+
server_id: common.ServerId
|
|
38
|
+
) -> common.EventId:
|
|
39
|
+
if server_id in self._last_event_ids:
|
|
40
|
+
return self._last_event_ids[server_id]
|
|
41
|
+
|
|
42
|
+
return common.EventId(server=server_id,
|
|
43
|
+
session=0,
|
|
44
|
+
instance=0)
|
|
45
|
+
|
|
46
|
+
def set_last_event_id(self, event_id: common.EventId):
|
|
47
|
+
self._changes.last_event_ids[event_id.server] = event_id
|
|
48
|
+
self._last_event_ids[event_id.server] = event_id
|
|
49
|
+
|
|
50
|
+
def get_last_timestamp(self,
|
|
51
|
+
server_id: common.ServerId
|
|
52
|
+
) -> common.Timestamp:
|
|
53
|
+
if server_id in self._last_timestamps:
|
|
54
|
+
return self._last_timestamps[server_id]
|
|
55
|
+
|
|
56
|
+
return common.min_timestamp
|
|
57
|
+
|
|
58
|
+
def set_last_timestamp(self,
|
|
59
|
+
server_id: common.ServerId,
|
|
60
|
+
timestamp: common.Timestamp):
|
|
61
|
+
self._changes.last_timestamps[server_id] = timestamp
|
|
62
|
+
self._last_timestamps[server_id] = timestamp
|
|
63
|
+
|
|
64
|
+
def create_changes(self) -> Changes:
|
|
65
|
+
changes, self._changes = self._changes, Changes({}, {})
|
|
66
|
+
return changes
|
|
67
|
+
|
|
68
|
+
def ext_write(self,
|
|
69
|
+
txn: lmdb.Transaction,
|
|
70
|
+
changes: Changes):
|
|
71
|
+
self._env.ext_write(txn, common.DbType.SYSTEM_LAST_EVENT_ID,
|
|
72
|
+
changes.last_event_ids.items())
|
|
73
|
+
|
|
74
|
+
self._env.ext_write(txn, common.DbType.SYSTEM_LAST_TIMESTAMP,
|
|
75
|
+
changes.last_timestamps.items())
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _ext_validate_settings(env, txn, version, identifier):
|
|
79
|
+
settings = dict(env.ext_read(txn, common.DbType.SYSTEM_SETTINGS))
|
|
80
|
+
|
|
81
|
+
if common.SettingsId.VERSION not in settings:
|
|
82
|
+
settings[common.SettingsId.VERSION] = version
|
|
83
|
+
|
|
84
|
+
elif settings[common.SettingsId.VERSION] != version:
|
|
85
|
+
raise Exception('invalid version')
|
|
86
|
+
|
|
87
|
+
else:
|
|
88
|
+
settings.pop(common.SettingsId.VERSION)
|
|
89
|
+
|
|
90
|
+
if identifier is None:
|
|
91
|
+
settings.pop(common.SettingsId.IDENTIFIER, None)
|
|
92
|
+
|
|
93
|
+
elif common.SettingsId.IDENTIFIER not in settings:
|
|
94
|
+
settings[common.SettingsId.IDENTIFIER] = identifier
|
|
95
|
+
|
|
96
|
+
elif settings[common.SettingsId.IDENTIFIER] != identifier:
|
|
97
|
+
raise Exception('invalid identifier')
|
|
98
|
+
|
|
99
|
+
else:
|
|
100
|
+
settings.pop(common.SettingsId.IDENTIFIER)
|
|
101
|
+
|
|
102
|
+
env.ext_write(txn, common.DbType.SYSTEM_SETTINGS, settings.items())
|