flo-python 0.1.0__py3-none-any.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.
- flo/__init__.py +214 -0
- flo/actions.py +397 -0
- flo/client.py +284 -0
- flo/exceptions.py +224 -0
- flo/kv.py +257 -0
- flo/queue.py +376 -0
- flo/streams.py +379 -0
- flo/types.py +804 -0
- flo/wire.py +926 -0
- flo/worker.py +421 -0
- flo_python-0.1.0.dist-info/METADATA +561 -0
- flo_python-0.1.0.dist-info/RECORD +13 -0
- flo_python-0.1.0.dist-info/WHEEL +4 -0
flo/types.py
ADDED
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
"""Flo SDK Types
|
|
2
|
+
|
|
3
|
+
Core types, constants, and data classes for the Flo client SDK.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import struct
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from enum import IntEnum
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
# =============================================================================
|
|
12
|
+
# Protocol Constants
|
|
13
|
+
# =============================================================================
|
|
14
|
+
|
|
15
|
+
MAGIC: int = 0x004F4C46 # "FLO\0" in little-endian
|
|
16
|
+
VERSION: int = 0x01
|
|
17
|
+
HEADER_SIZE: int = 24
|
|
18
|
+
|
|
19
|
+
# Size limits (for client-side validation)
|
|
20
|
+
MAX_NAMESPACE_SIZE: int = 255
|
|
21
|
+
MAX_KEY_SIZE: int = 64 * 1024 # 64 KB
|
|
22
|
+
MAX_VALUE_SIZE: int = 16 * 1024 * 1024 # 16 MB practical limit
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# =============================================================================
|
|
26
|
+
# Enums
|
|
27
|
+
# =============================================================================
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class OpCode(IntEnum):
|
|
31
|
+
"""Operation codes for Flo protocol requests."""
|
|
32
|
+
|
|
33
|
+
# System Operations (0x00 - 0x0F)
|
|
34
|
+
PING = 0x00
|
|
35
|
+
PONG = 0x01
|
|
36
|
+
ERROR_RESPONSE = 0x02
|
|
37
|
+
AUTH = 0x03
|
|
38
|
+
SET_DURABILITY = 0x04
|
|
39
|
+
OK = 0x05
|
|
40
|
+
|
|
41
|
+
# Streams (0x10 - 0x1F)
|
|
42
|
+
STREAM_APPEND = 0x10
|
|
43
|
+
STREAM_READ = 0x11
|
|
44
|
+
STREAM_TRIM = 0x12
|
|
45
|
+
STREAM_INFO = 0x13
|
|
46
|
+
STREAM_APPEND_RESPONSE = 0x14
|
|
47
|
+
STREAM_READ_RESPONSE = 0x15
|
|
48
|
+
STREAM_EVENT = 0x16
|
|
49
|
+
STREAM_SUBSCRIBE = 0x17
|
|
50
|
+
STREAM_UNSUBSCRIBE = 0x18
|
|
51
|
+
STREAM_SUBSCRIBED = 0x19
|
|
52
|
+
STREAM_UNSUBSCRIBED = 0x1A
|
|
53
|
+
STREAM_LIST = 0x1B
|
|
54
|
+
STREAM_LIST_RESPONSE = 0x1C
|
|
55
|
+
STREAM_CREATE = 0x1D
|
|
56
|
+
STREAM_CREATE_RESPONSE = 0x1E
|
|
57
|
+
STREAM_ALTER = 0x1F
|
|
58
|
+
|
|
59
|
+
# Stream Consumer Groups (0x20 - 0x2F)
|
|
60
|
+
STREAM_GROUP_CREATE = 0x20
|
|
61
|
+
STREAM_GROUP_JOIN = 0x21
|
|
62
|
+
STREAM_GROUP_LEAVE = 0x22
|
|
63
|
+
STREAM_GROUP_READ = 0x23
|
|
64
|
+
STREAM_GROUP_ACK = 0x24
|
|
65
|
+
STREAM_GROUP_CLAIM = 0x25
|
|
66
|
+
STREAM_GROUP_PENDING = 0x26
|
|
67
|
+
STREAM_GROUP_CONFIGURE_SWEEPER = 0x27
|
|
68
|
+
STREAM_GROUP_READ_RESPONSE = 0x28
|
|
69
|
+
STREAM_GROUP_NACK = 0x29
|
|
70
|
+
STREAM_GROUP_TOUCH = 0x2A
|
|
71
|
+
STREAM_GROUP_INFO = 0x2B
|
|
72
|
+
STREAM_GROUP_DELETE = 0x2C
|
|
73
|
+
|
|
74
|
+
# KV Operations (0x30 - 0x3F)
|
|
75
|
+
KV_PUT = 0x30
|
|
76
|
+
KV_GET = 0x31
|
|
77
|
+
KV_DELETE = 0x32
|
|
78
|
+
KV_SCAN = 0x33
|
|
79
|
+
KV_HISTORY = 0x34
|
|
80
|
+
KV_GET_RESPONSE = 0x35
|
|
81
|
+
KV_PUT_RESPONSE = 0x36
|
|
82
|
+
KV_SCAN_RESPONSE = 0x37
|
|
83
|
+
KV_HISTORY_RESPONSE = 0x38
|
|
84
|
+
|
|
85
|
+
# Transactions (0x39 - 0x3B)
|
|
86
|
+
KV_BEGIN_TXN = 0x39
|
|
87
|
+
KV_COMMIT_TXN = 0x3A
|
|
88
|
+
KV_ROLLBACK_TXN = 0x3B
|
|
89
|
+
|
|
90
|
+
# Snapshots (0x3C - 0x3F)
|
|
91
|
+
KV_SNAPSHOT_CREATE = 0x3C
|
|
92
|
+
KV_SNAPSHOT_GET = 0x3D
|
|
93
|
+
KV_SNAPSHOT_RELEASE = 0x3E
|
|
94
|
+
KV_SNAPSHOT_CREATE_RESPONSE = 0x3F
|
|
95
|
+
|
|
96
|
+
# Queues (0x40 - 0x5F)
|
|
97
|
+
QUEUE_ENQUEUE = 0x40
|
|
98
|
+
QUEUE_DEQUEUE = 0x41
|
|
99
|
+
QUEUE_COMPLETE = 0x42
|
|
100
|
+
QUEUE_EXTEND_LEASE = 0x43
|
|
101
|
+
QUEUE_FAIL = 0x44
|
|
102
|
+
QUEUE_FAIL_AUTO = 0x45
|
|
103
|
+
QUEUE_DLQ_LIST = 0x46
|
|
104
|
+
QUEUE_DLQ_DELETE = 0x47
|
|
105
|
+
QUEUE_DLQ_REQUEUE = 0x48
|
|
106
|
+
QUEUE_DLQ_STATS = 0x49
|
|
107
|
+
QUEUE_PROMOTE_DUE = 0x4A
|
|
108
|
+
QUEUE_STATS = 0x4B
|
|
109
|
+
QUEUE_PEEK = 0x4C
|
|
110
|
+
QUEUE_TOUCH = 0x4D
|
|
111
|
+
QUEUE_BATCH_ENQUEUE = 0x4E
|
|
112
|
+
QUEUE_PURGE = 0x4F
|
|
113
|
+
|
|
114
|
+
# Queue responses (0x50 - 0x5F)
|
|
115
|
+
QUEUE_ENQUEUE_RESPONSE = 0x50
|
|
116
|
+
QUEUE_DEQUEUE_RESPONSE = 0x51
|
|
117
|
+
QUEUE_DLQ_LIST_RESPONSE = 0x52
|
|
118
|
+
QUEUE_STATS_RESPONSE = 0x53
|
|
119
|
+
QUEUE_PEEK_RESPONSE = 0x54
|
|
120
|
+
QUEUE_TOUCH_RESPONSE = 0x55
|
|
121
|
+
QUEUE_BATCH_ENQUEUE_RESPONSE = 0x56
|
|
122
|
+
QUEUE_PURGE_RESPONSE = 0x57
|
|
123
|
+
|
|
124
|
+
# Actions (0x60 - 0x68)
|
|
125
|
+
ACTION_REGISTER = 0x60
|
|
126
|
+
ACTION_INVOKE = 0x61
|
|
127
|
+
ACTION_STATUS = 0x62
|
|
128
|
+
ACTION_LIST = 0x63
|
|
129
|
+
ACTION_DELETE = 0x64
|
|
130
|
+
ACTION_REGISTER_RESPONSE = 0x65
|
|
131
|
+
ACTION_INVOKE_RESPONSE = 0x66
|
|
132
|
+
ACTION_STATUS_RESPONSE = 0x67
|
|
133
|
+
ACTION_LIST_RESPONSE = 0x68
|
|
134
|
+
|
|
135
|
+
# Workers (0x69 - 0x7F)
|
|
136
|
+
WORKER_REGISTER = 0x69
|
|
137
|
+
WORKER_TOUCH = 0x6A
|
|
138
|
+
WORKER_AWAIT = 0x6B
|
|
139
|
+
WORKER_COMPLETE = 0x6C
|
|
140
|
+
WORKER_FAIL = 0x6D
|
|
141
|
+
WORKER_LIST = 0x6E
|
|
142
|
+
WORKER_REGISTER_RESPONSE = 0x70
|
|
143
|
+
WORKER_TASK_ASSIGNMENT = 0x71
|
|
144
|
+
WORKER_LIST_RESPONSE = 0x72
|
|
145
|
+
|
|
146
|
+
# Workflows (0x80 - 0x91)
|
|
147
|
+
WORKFLOW_CREATE = 0x80
|
|
148
|
+
WORKFLOW_START = 0x81
|
|
149
|
+
WORKFLOW_SIGNAL = 0x82
|
|
150
|
+
WORKFLOW_CANCEL = 0x83
|
|
151
|
+
WORKFLOW_STATUS = 0x84
|
|
152
|
+
WORKFLOW_HISTORY = 0x85
|
|
153
|
+
WORKFLOW_LIST_RUNS = 0x86
|
|
154
|
+
WORKFLOW_GET_DEFINITION = 0x87
|
|
155
|
+
WORKFLOW_CREATE_RESPONSE = 0x88
|
|
156
|
+
WORKFLOW_START_RESPONSE = 0x89
|
|
157
|
+
WORKFLOW_STATUS_RESPONSE = 0x8A
|
|
158
|
+
WORKFLOW_HISTORY_RESPONSE = 0x8B
|
|
159
|
+
WORKFLOW_LIST_RUNS_RESPONSE = 0x8C
|
|
160
|
+
WORKFLOW_GET_DEFINITION_RESPONSE = 0x8D
|
|
161
|
+
WORKFLOW_DISABLE = 0x8E
|
|
162
|
+
WORKFLOW_ENABLE = 0x8F
|
|
163
|
+
WORKFLOW_DISABLE_RESPONSE = 0x90
|
|
164
|
+
WORKFLOW_ENABLE_RESPONSE = 0x91
|
|
165
|
+
|
|
166
|
+
# Cluster Management (0xA0 - 0xAF)
|
|
167
|
+
CLUSTER_STATUS = 0xA0
|
|
168
|
+
CLUSTER_MEMBERS = 0xA1
|
|
169
|
+
CLUSTER_JOIN = 0xA2
|
|
170
|
+
CLUSTER_LEAVE = 0xA3
|
|
171
|
+
CLUSTER_TRANSFER_LEADER = 0xA4
|
|
172
|
+
CLUSTER_ADD_NODE = 0xA5
|
|
173
|
+
CLUSTER_REMOVE_NODE = 0xA6
|
|
174
|
+
CLUSTER_STATUS_RESPONSE = 0xA8
|
|
175
|
+
CLUSTER_MEMBERS_RESPONSE = 0xA9
|
|
176
|
+
CLUSTER_JOIN_RESPONSE = 0xAA
|
|
177
|
+
|
|
178
|
+
# Namespace Management (0xB0 - 0xBF)
|
|
179
|
+
NAMESPACE_CREATE = 0xB0
|
|
180
|
+
NAMESPACE_DELETE = 0xB1
|
|
181
|
+
NAMESPACE_LIST = 0xB2
|
|
182
|
+
NAMESPACE_INFO = 0xB3
|
|
183
|
+
NAMESPACE_CREATE_RESPONSE = 0xB4
|
|
184
|
+
NAMESPACE_DELETE_RESPONSE = 0xB5
|
|
185
|
+
NAMESPACE_LIST_RESPONSE = 0xB6
|
|
186
|
+
NAMESPACE_INFO_RESPONSE = 0xB7
|
|
187
|
+
|
|
188
|
+
# Processing / Stream Processing (0xC0 - 0xD1)
|
|
189
|
+
PROCESSING_SUBMIT = 0xC0
|
|
190
|
+
PROCESSING_STOP = 0xC1
|
|
191
|
+
PROCESSING_CANCEL = 0xC2
|
|
192
|
+
PROCESSING_STATUS = 0xC3
|
|
193
|
+
PROCESSING_LIST = 0xC4
|
|
194
|
+
PROCESSING_SAVEPOINT = 0xC6
|
|
195
|
+
PROCESSING_RESTORE = 0xC7
|
|
196
|
+
PROCESSING_RESCALE = 0xC8
|
|
197
|
+
PROCESSING_SUBMIT_RESPONSE = 0xC9
|
|
198
|
+
PROCESSING_STOP_RESPONSE = 0xCA
|
|
199
|
+
PROCESSING_CANCEL_RESPONSE = 0xCB
|
|
200
|
+
PROCESSING_STATUS_RESPONSE = 0xCC
|
|
201
|
+
PROCESSING_LIST_RESPONSE = 0xCD
|
|
202
|
+
PROCESSING_SAVEPOINT_RESPONSE = 0xCF
|
|
203
|
+
PROCESSING_RESTORE_RESPONSE = 0xD0
|
|
204
|
+
PROCESSING_RESCALE_RESPONSE = 0xD1
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class StatusCode(IntEnum):
|
|
208
|
+
"""Status codes for Flo protocol responses."""
|
|
209
|
+
|
|
210
|
+
OK = 0
|
|
211
|
+
ERROR_GENERIC = 1
|
|
212
|
+
NOT_FOUND = 2
|
|
213
|
+
BAD_REQUEST = 3
|
|
214
|
+
CROSS_CORE_TRANSACTION = 4
|
|
215
|
+
NO_ACTIVE_TRANSACTION = 5
|
|
216
|
+
GROUP_LOCKED = 6
|
|
217
|
+
UNAUTHORIZED = 7
|
|
218
|
+
CONFLICT = 8
|
|
219
|
+
INTERNAL_ERROR = 9
|
|
220
|
+
OVERLOADED = 10
|
|
221
|
+
RATE_LIMITED = 11
|
|
222
|
+
|
|
223
|
+
def message(self) -> str:
|
|
224
|
+
"""Get human-readable error message."""
|
|
225
|
+
messages = {
|
|
226
|
+
StatusCode.OK: "OK",
|
|
227
|
+
StatusCode.ERROR_GENERIC: "Generic error",
|
|
228
|
+
StatusCode.NOT_FOUND: "Not found",
|
|
229
|
+
StatusCode.BAD_REQUEST: "Bad request",
|
|
230
|
+
StatusCode.CROSS_CORE_TRANSACTION: "Cross-core transaction not supported",
|
|
231
|
+
StatusCode.NO_ACTIVE_TRANSACTION: "No active transaction",
|
|
232
|
+
StatusCode.GROUP_LOCKED: "Consumer group is locked",
|
|
233
|
+
StatusCode.UNAUTHORIZED: "Unauthorized",
|
|
234
|
+
StatusCode.CONFLICT: "Conflict",
|
|
235
|
+
StatusCode.INTERNAL_ERROR: "Internal server error",
|
|
236
|
+
StatusCode.OVERLOADED: "Server overloaded",
|
|
237
|
+
StatusCode.RATE_LIMITED: "Request rate limit exceeded",
|
|
238
|
+
}
|
|
239
|
+
return messages.get(self, "Unknown error")
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class OptionTag(IntEnum):
|
|
243
|
+
"""Option tags for TLV-encoded operation parameters.
|
|
244
|
+
|
|
245
|
+
Organized by feature area, matching proto.zig definitions.
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
# KV Options (0x01 - 0x0F)
|
|
249
|
+
TTL_SECONDS = 0x01 # u64: Time-to-live in seconds (0 = no expiration)
|
|
250
|
+
CAS_VERSION = 0x02 # u64: Expected version for compare-and-swap
|
|
251
|
+
IF_NOT_EXISTS = 0x03 # void: Only set if key doesn't exist (NX)
|
|
252
|
+
IF_EXISTS = 0x04 # void: Only set if key exists (XX)
|
|
253
|
+
LIMIT = 0x05 # u32: Maximum number of results for scan/list operations
|
|
254
|
+
KEYS_ONLY = 0x06 # u8: Skip values in scan response (0/1)
|
|
255
|
+
CURSOR = 0x07 # bytes: Pagination cursor (ShardWalker format)
|
|
256
|
+
|
|
257
|
+
# Queue Options (0x10 - 0x1F)
|
|
258
|
+
PRIORITY = 0x10 # u8: Message priority (0-255, higher = more urgent)
|
|
259
|
+
DELAY_MS = 0x11 # u64: Delay before message becomes visible
|
|
260
|
+
VISIBILITY_TIMEOUT_MS = 0x12 # u32: How long message is invisible after dequeue
|
|
261
|
+
DEDUP_KEY = 0x13 # string: Deduplication key
|
|
262
|
+
MAX_RETRIES = 0x14 # u8: Maximum retry attempts before DLQ
|
|
263
|
+
COUNT = 0x15 # u32: Number of messages to dequeue
|
|
264
|
+
SEND_TO_DLQ = 0x16 # u8: Whether to send failed messages to DLQ (0/1)
|
|
265
|
+
BLOCK_MS = 0x17 # u32: Block timeout - wait until exists (0=forever)
|
|
266
|
+
WAIT_MS = 0x18 # u32: Watch timeout - wait for NEXT version change (0=forever)
|
|
267
|
+
|
|
268
|
+
# Stream Options (0x20 - 0x2F) - StreamID-native ONLY
|
|
269
|
+
# All stream positioning uses StreamID (timestamp_ms + sequence)
|
|
270
|
+
# 0x20 reserved
|
|
271
|
+
STREAM_START = 0x21 # [16]u8: Start StreamID for reads (inclusive)
|
|
272
|
+
STREAM_END = 0x22 # [16]u8: End StreamID for reads (inclusive)
|
|
273
|
+
STREAM_TAIL = 0x23 # void: Flag indicating tail read (start from end)
|
|
274
|
+
PARTITION = 0x24 # u32: Explicit partition index
|
|
275
|
+
PARTITION_KEY = 0x25 # string: Key for partition routing
|
|
276
|
+
MAX_AGE_SECONDS = 0x26 # u64: Maximum age in seconds for retention
|
|
277
|
+
MAX_BYTES = 0x27 # u64: Maximum size in bytes for retention
|
|
278
|
+
DRY_RUN = 0x28 # void: Flag to preview what would be deleted
|
|
279
|
+
RETENTION_COUNT = 0x29 # u64: Retention policy - max event count
|
|
280
|
+
RETENTION_AGE = 0x2A # u64: Retention policy - max age in seconds
|
|
281
|
+
RETENTION_BYTES = 0x2B # u64: Retention policy - max bytes
|
|
282
|
+
|
|
283
|
+
# Consumer Group Options (0x30 - 0x3F)
|
|
284
|
+
ACK_TIMEOUT_MS = 0x30 # u32: Time before unacked message auto-redelivers
|
|
285
|
+
MAX_DELIVER = 0x31 # u8: Max delivery attempts before DLQ (default: 10)
|
|
286
|
+
SUBSCRIPTION_MODE = 0x32 # u8: 0=shared, 1=exclusive, 2=key_shared
|
|
287
|
+
REDELIVERY_DELAY_MS = 0x33 # u32: Delay before NACK'd message becomes visible
|
|
288
|
+
CONSUMER_TIMEOUT_MS = 0x34 # u32: Remove consumer from group if no activity
|
|
289
|
+
NO_ACK = 0x35 # void: Auto-ack on delivery (at-most-once)
|
|
290
|
+
IDLE_TIMEOUT_MS = 0x36 # u64: Min idle time for claiming stuck messages
|
|
291
|
+
MAX_ACK_PENDING = 0x37 # u32: Max unacked messages per consumer
|
|
292
|
+
EXTEND_ACK_MS = 0x38 # u32: Amount of time to extend ack deadline
|
|
293
|
+
MAX_STANDBYS = 0x39 # u16: Max standby consumers in exclusive mode
|
|
294
|
+
NUM_SLOTS = 0x3A # u16: Number of hash slots for key_shared mode
|
|
295
|
+
|
|
296
|
+
# Worker/Action Options (0x40 - 0x4F)
|
|
297
|
+
WORKER_ID = 0x40 # string: Worker identifier
|
|
298
|
+
EXTEND_MS = 0x41 # u32: Lease extension time in milliseconds
|
|
299
|
+
MAX_TASKS = 0x42 # u32: Maximum tasks to return in batch
|
|
300
|
+
RETRY = 0x43 # u8: Whether to retry on failure (0/1)
|
|
301
|
+
|
|
302
|
+
# Workflow Options (0x50 - 0x5F)
|
|
303
|
+
TIMEOUT_MS = 0x50 # u64: Workflow/activity timeout
|
|
304
|
+
RETRY_POLICY = 0x51 # bytes: Serialized retry policy
|
|
305
|
+
CORRELATION_ID = 0x52 # string: Correlation ID for tracing
|
|
306
|
+
SUBSCRIPTION_ID = 0x53 # u64: Subscription ID for stream subscriptions
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
# =============================================================================
|
|
310
|
+
# Result Types
|
|
311
|
+
# =============================================================================
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@dataclass
|
|
315
|
+
class KVEntry:
|
|
316
|
+
"""KV entry from scan results."""
|
|
317
|
+
|
|
318
|
+
key: bytes
|
|
319
|
+
value: bytes | None # None if keys_only=True
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
@dataclass
|
|
323
|
+
class ScanResult:
|
|
324
|
+
"""Result of a KV scan operation."""
|
|
325
|
+
|
|
326
|
+
entries: list[KVEntry]
|
|
327
|
+
cursor: bytes | None # None if no more pages
|
|
328
|
+
has_more: bool
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
@dataclass
|
|
332
|
+
class VersionEntry:
|
|
333
|
+
"""KV version entry from history."""
|
|
334
|
+
|
|
335
|
+
version: int
|
|
336
|
+
timestamp: int
|
|
337
|
+
value: bytes
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
@dataclass
|
|
341
|
+
class Message:
|
|
342
|
+
"""Queue message."""
|
|
343
|
+
|
|
344
|
+
seq: int
|
|
345
|
+
payload: bytes
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
@dataclass
|
|
349
|
+
class DequeueResult:
|
|
350
|
+
"""Result of a queue dequeue operation."""
|
|
351
|
+
|
|
352
|
+
messages: list[Message]
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
# =============================================================================
|
|
356
|
+
# Stream Types
|
|
357
|
+
# =============================================================================
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
@dataclass
|
|
361
|
+
class StreamID:
|
|
362
|
+
"""Unique position in a stream (timestamp_ms + sequence).
|
|
363
|
+
|
|
364
|
+
The StreamID format is: [timestamp_ms: u64][sequence: u64] = 16 bytes total.
|
|
365
|
+
"""
|
|
366
|
+
|
|
367
|
+
timestamp_ms: int = 0
|
|
368
|
+
sequence: int = 0
|
|
369
|
+
|
|
370
|
+
def to_bytes(self) -> bytes:
|
|
371
|
+
"""Serialize the StreamID to 16 bytes (little-endian)."""
|
|
372
|
+
return struct.pack("<QQ", self.timestamp_ms, self.sequence)
|
|
373
|
+
|
|
374
|
+
@classmethod
|
|
375
|
+
def from_bytes(cls, data: bytes) -> "StreamID":
|
|
376
|
+
"""Parse a StreamID from 16 bytes (little-endian)."""
|
|
377
|
+
if len(data) < 16:
|
|
378
|
+
raise ValueError(f"Invalid StreamID: expected 16 bytes, got {len(data)}")
|
|
379
|
+
ts, seq = struct.unpack("<QQ", data[:16])
|
|
380
|
+
return cls(timestamp_ms=ts, sequence=seq)
|
|
381
|
+
|
|
382
|
+
@classmethod
|
|
383
|
+
def from_sequence(cls, seq: int) -> "StreamID":
|
|
384
|
+
"""Create a StreamID with just a sequence number.
|
|
385
|
+
|
|
386
|
+
Used for backwards compatibility with offset-based reads.
|
|
387
|
+
"""
|
|
388
|
+
return cls(timestamp_ms=0, sequence=seq)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
class StorageTier(IntEnum):
|
|
392
|
+
"""Storage tier of a stream record."""
|
|
393
|
+
|
|
394
|
+
HOT = 0
|
|
395
|
+
PENDING = 1
|
|
396
|
+
WARM = 2
|
|
397
|
+
COLD = 3
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
@dataclass
|
|
401
|
+
class StreamRecord:
|
|
402
|
+
"""A record in a stream."""
|
|
403
|
+
|
|
404
|
+
sequence: int
|
|
405
|
+
timestamp_ms: int
|
|
406
|
+
tier: StorageTier = StorageTier.HOT
|
|
407
|
+
payload: bytes = b""
|
|
408
|
+
headers: dict[str, str] | None = None
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
@dataclass
|
|
412
|
+
class StreamAppendResult:
|
|
413
|
+
"""Result of appending to a stream."""
|
|
414
|
+
|
|
415
|
+
sequence: int
|
|
416
|
+
timestamp_ms: int
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
@dataclass
|
|
420
|
+
class StreamReadResult:
|
|
421
|
+
"""Result of reading from a stream."""
|
|
422
|
+
|
|
423
|
+
records: list[StreamRecord]
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
@dataclass
|
|
427
|
+
class StreamInfo:
|
|
428
|
+
"""Stream metadata."""
|
|
429
|
+
|
|
430
|
+
first_seq: int
|
|
431
|
+
last_seq: int
|
|
432
|
+
count: int
|
|
433
|
+
bytes_size: int
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
# =============================================================================
|
|
437
|
+
# Option Types (for operation parameters)
|
|
438
|
+
# =============================================================================
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
@dataclass
|
|
442
|
+
class GetOptions:
|
|
443
|
+
"""Options for KV get operations."""
|
|
444
|
+
|
|
445
|
+
namespace: str | None = None
|
|
446
|
+
block_ms: int | None = None # Block until value available (0 = infinite)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
@dataclass
|
|
450
|
+
class PutOptions:
|
|
451
|
+
"""Options for KV put operations."""
|
|
452
|
+
|
|
453
|
+
namespace: str | None = None
|
|
454
|
+
ttl_seconds: int | None = None
|
|
455
|
+
cas_version: int | None = None
|
|
456
|
+
if_not_exists: bool = False
|
|
457
|
+
if_exists: bool = False
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
@dataclass
|
|
461
|
+
class DeleteOptions:
|
|
462
|
+
"""Options for KV delete operations."""
|
|
463
|
+
|
|
464
|
+
namespace: str | None = None
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
@dataclass
|
|
468
|
+
class ScanOptions:
|
|
469
|
+
"""Options for KV scan operations."""
|
|
470
|
+
|
|
471
|
+
namespace: str | None = None
|
|
472
|
+
cursor: bytes | None = None
|
|
473
|
+
limit: int | None = None
|
|
474
|
+
keys_only: bool = False
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
@dataclass
|
|
478
|
+
class HistoryOptions:
|
|
479
|
+
"""Options for KV history operations."""
|
|
480
|
+
|
|
481
|
+
namespace: str | None = None
|
|
482
|
+
limit: int | None = None
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
@dataclass
|
|
486
|
+
class EnqueueOptions:
|
|
487
|
+
"""Options for queue enqueue operations."""
|
|
488
|
+
|
|
489
|
+
namespace: str | None = None
|
|
490
|
+
priority: int = 0
|
|
491
|
+
delay_ms: int | None = None
|
|
492
|
+
dedup_key: str | None = None
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
@dataclass
|
|
496
|
+
class DequeueOptions:
|
|
497
|
+
"""Options for queue dequeue operations."""
|
|
498
|
+
|
|
499
|
+
namespace: str | None = None
|
|
500
|
+
visibility_timeout_ms: int | None = None
|
|
501
|
+
block_ms: int | None = None
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
@dataclass
|
|
505
|
+
class AckOptions:
|
|
506
|
+
"""Options for queue ack operations."""
|
|
507
|
+
|
|
508
|
+
namespace: str | None = None
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
@dataclass
|
|
512
|
+
class NackOptions:
|
|
513
|
+
"""Options for queue nack operations."""
|
|
514
|
+
|
|
515
|
+
namespace: str | None = None
|
|
516
|
+
to_dlq: bool = False
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
@dataclass
|
|
520
|
+
class DlqListOptions:
|
|
521
|
+
"""Options for DLQ list operations."""
|
|
522
|
+
|
|
523
|
+
namespace: str | None = None
|
|
524
|
+
limit: int = 100
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
@dataclass
|
|
528
|
+
class DlqRequeueOptions:
|
|
529
|
+
"""Options for DLQ requeue operations."""
|
|
530
|
+
|
|
531
|
+
namespace: str | None = None
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
@dataclass
|
|
535
|
+
class PeekOptions:
|
|
536
|
+
"""Options for queue peek operations."""
|
|
537
|
+
|
|
538
|
+
namespace: str | None = None
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
@dataclass
|
|
542
|
+
class TouchOptions:
|
|
543
|
+
"""Options for queue touch (lease renewal) operations."""
|
|
544
|
+
|
|
545
|
+
namespace: str | None = None
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
# =============================================================================
|
|
549
|
+
# Stream Option Types
|
|
550
|
+
# =============================================================================
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
@dataclass
|
|
554
|
+
class StreamAppendOptions:
|
|
555
|
+
"""Options for stream append operations."""
|
|
556
|
+
|
|
557
|
+
namespace: str | None = None
|
|
558
|
+
headers: dict[str, str] | None = None
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
@dataclass
|
|
562
|
+
class StreamReadOptions:
|
|
563
|
+
"""Options for stream read operations.
|
|
564
|
+
|
|
565
|
+
Uses StreamID-native positioning (timestamp_ms + sequence).
|
|
566
|
+
"""
|
|
567
|
+
|
|
568
|
+
namespace: str | None = None
|
|
569
|
+
start: Optional["StreamID"] = None # Start StreamID for reads (inclusive)
|
|
570
|
+
end: Optional["StreamID"] = None # End StreamID for reads (inclusive)
|
|
571
|
+
tail: bool = False # Start from end of stream (mutually exclusive with start)
|
|
572
|
+
partition: int | None = None # Explicit partition index
|
|
573
|
+
count: int | None = None # Maximum number of records to return
|
|
574
|
+
block_ms: int | None = None # Blocking timeout (0 = infinite)
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
@dataclass
|
|
578
|
+
class StreamTrimOptions:
|
|
579
|
+
"""Options for stream trim operations."""
|
|
580
|
+
|
|
581
|
+
namespace: str | None = None
|
|
582
|
+
max_len: int | None = None # Retention policy - max event count
|
|
583
|
+
max_age_seconds: int | None = None # Retention policy - max age in seconds
|
|
584
|
+
max_bytes: int | None = None # Retention policy - max bytes
|
|
585
|
+
dry_run: bool = False # Preview what would be deleted
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
@dataclass
|
|
589
|
+
class StreamInfoOptions:
|
|
590
|
+
"""Options for stream info operations."""
|
|
591
|
+
|
|
592
|
+
namespace: str | None = None
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
@dataclass
|
|
596
|
+
class StreamGroupJoinOptions:
|
|
597
|
+
"""Options for joining a consumer group."""
|
|
598
|
+
|
|
599
|
+
namespace: str | None = None
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
@dataclass
|
|
603
|
+
class StreamGroupReadOptions:
|
|
604
|
+
"""Options for reading from a consumer group."""
|
|
605
|
+
|
|
606
|
+
namespace: str | None = None
|
|
607
|
+
count: int | None = None # Max records to read
|
|
608
|
+
block_ms: int | None = None # Block waiting for records
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
@dataclass
|
|
612
|
+
class StreamGroupAckOptions:
|
|
613
|
+
"""Options for acknowledging records in a consumer group."""
|
|
614
|
+
|
|
615
|
+
namespace: str | None = None
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
# =============================================================================
|
|
619
|
+
# Action Types
|
|
620
|
+
# =============================================================================
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
class ActionType(IntEnum):
|
|
624
|
+
"""Type of action."""
|
|
625
|
+
|
|
626
|
+
USER = 0 # External worker-based action
|
|
627
|
+
WASM = 1 # WebAssembly action
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
@dataclass
|
|
631
|
+
class ActionInfo:
|
|
632
|
+
"""Information about a registered action."""
|
|
633
|
+
|
|
634
|
+
name: str
|
|
635
|
+
action_type: ActionType
|
|
636
|
+
timeout_ms: int
|
|
637
|
+
max_retries: int
|
|
638
|
+
description: str | None = None
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
@dataclass
|
|
642
|
+
class ActionRunStatus:
|
|
643
|
+
"""Status of an action run."""
|
|
644
|
+
|
|
645
|
+
run_id: str
|
|
646
|
+
status: str # "pending", "running", "completed", "failed"
|
|
647
|
+
result: bytes | None = None
|
|
648
|
+
error: str | None = None
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
@dataclass
|
|
652
|
+
class ActionInvokeResult:
|
|
653
|
+
"""Result of invoking an action."""
|
|
654
|
+
|
|
655
|
+
run_id: str
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
@dataclass
|
|
659
|
+
class ActionListResult:
|
|
660
|
+
"""Result of listing actions."""
|
|
661
|
+
|
|
662
|
+
actions: list[ActionInfo]
|
|
663
|
+
cursor: bytes | None = None
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
# =============================================================================
|
|
667
|
+
# Action Option Types
|
|
668
|
+
# =============================================================================
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
@dataclass
|
|
672
|
+
class ActionRegisterOptions:
|
|
673
|
+
"""Options for registering an action."""
|
|
674
|
+
|
|
675
|
+
namespace: str | None = None
|
|
676
|
+
timeout_ms: int = 30000
|
|
677
|
+
max_retries: int = 3
|
|
678
|
+
description: str | None = None
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
@dataclass
|
|
682
|
+
class ActionInvokeOptions:
|
|
683
|
+
"""Options for invoking an action."""
|
|
684
|
+
|
|
685
|
+
namespace: str | None = None
|
|
686
|
+
priority: int = 10
|
|
687
|
+
idempotency_key: str | None = None
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
@dataclass
|
|
691
|
+
class ActionStatusOptions:
|
|
692
|
+
"""Options for getting action status."""
|
|
693
|
+
|
|
694
|
+
namespace: str | None = None
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
@dataclass
|
|
698
|
+
class ActionListOptions:
|
|
699
|
+
"""Options for listing actions."""
|
|
700
|
+
|
|
701
|
+
namespace: str | None = None
|
|
702
|
+
limit: int = 100
|
|
703
|
+
prefix: str | None = None
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
@dataclass
|
|
707
|
+
class ActionDeleteOptions:
|
|
708
|
+
"""Options for deleting an action."""
|
|
709
|
+
|
|
710
|
+
namespace: str | None = None
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
# =============================================================================
|
|
714
|
+
# Worker Types
|
|
715
|
+
# =============================================================================
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
@dataclass
|
|
719
|
+
class TaskAssignment:
|
|
720
|
+
"""A task assigned to a worker."""
|
|
721
|
+
|
|
722
|
+
task_id: str
|
|
723
|
+
task_type: str
|
|
724
|
+
payload: bytes
|
|
725
|
+
created_at: int
|
|
726
|
+
attempt: int
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
# Alias for backwards compatibility
|
|
730
|
+
WorkerTask = TaskAssignment
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
@dataclass
|
|
734
|
+
class WorkerAwaitResult:
|
|
735
|
+
"""Result of awaiting a task."""
|
|
736
|
+
|
|
737
|
+
task: TaskAssignment | None = None # None if no task available
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
@dataclass
|
|
741
|
+
class WorkerInfo:
|
|
742
|
+
"""Information about a registered worker."""
|
|
743
|
+
|
|
744
|
+
worker_id: str
|
|
745
|
+
task_types: list[str]
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
@dataclass
|
|
749
|
+
class WorkerListResult:
|
|
750
|
+
"""Result of listing workers."""
|
|
751
|
+
|
|
752
|
+
workers: list[WorkerInfo]
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
# =============================================================================
|
|
756
|
+
# Worker Option Types
|
|
757
|
+
# =============================================================================
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
@dataclass
|
|
761
|
+
class WorkerRegisterOptions:
|
|
762
|
+
"""Options for registering a worker."""
|
|
763
|
+
|
|
764
|
+
namespace: str | None = None
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
@dataclass
|
|
768
|
+
class WorkerAwaitOptions:
|
|
769
|
+
"""Options for awaiting a task."""
|
|
770
|
+
|
|
771
|
+
namespace: str | None = None
|
|
772
|
+
block_ms: int | None = None # Block waiting for task (0 = infinite)
|
|
773
|
+
timeout_ms: int | None = None
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
@dataclass
|
|
777
|
+
class WorkerTouchOptions:
|
|
778
|
+
"""Options for extending task lease."""
|
|
779
|
+
|
|
780
|
+
namespace: str | None = None
|
|
781
|
+
extend_ms: int = 30000
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
@dataclass
|
|
785
|
+
class WorkerCompleteOptions:
|
|
786
|
+
"""Options for completing a task."""
|
|
787
|
+
|
|
788
|
+
namespace: str | None = None
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
@dataclass
|
|
792
|
+
class WorkerFailOptions:
|
|
793
|
+
"""Options for failing a task."""
|
|
794
|
+
|
|
795
|
+
namespace: str | None = None
|
|
796
|
+
retry: bool = True # Whether to retry the task
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
@dataclass
|
|
800
|
+
class WorkerListOptions:
|
|
801
|
+
"""Options for listing workers."""
|
|
802
|
+
|
|
803
|
+
namespace: str | None = None
|
|
804
|
+
limit: int = 100
|