flo-python 0.1.0.dev4__py3-none-any.whl → 0.1.2__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 +60 -15
- flo/actions.py +20 -27
- flo/client.py +193 -46
- flo/exceptions.py +83 -23
- flo/kv.py +373 -165
- flo/kv_body.py +554 -0
- flo/kv_txn.py +237 -0
- flo/processing.py +3 -67
- flo/queue.py +25 -101
- flo/retry.py +124 -0
- flo/streams.py +146 -31
- flo/types.py +431 -221
- flo/wire.py +263 -208
- flo/worker.py +684 -250
- {flo_python-0.1.0.dev4.dist-info → flo_python-0.1.2.dist-info}/METADATA +165 -98
- flo_python-0.1.2.dist-info/RECORD +19 -0
- {flo_python-0.1.0.dev4.dist-info → flo_python-0.1.2.dist-info}/WHEEL +1 -1
- flo_python-0.1.2.dist-info/licenses/LICENSE +202 -0
- flo_python-0.1.0.dev4.dist-info/RECORD +0 -16
- flo_python-0.1.0.dev4.dist-info/licenses/LICENSE +0 -21
flo/types.py
CHANGED
|
@@ -6,20 +6,102 @@ Core types, constants, and data classes for the Flo client SDK.
|
|
|
6
6
|
import struct
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from enum import IntEnum
|
|
9
|
-
from typing import Optional
|
|
9
|
+
from typing import Any, Generic, Optional, TypeVar
|
|
10
10
|
|
|
11
11
|
# =============================================================================
|
|
12
12
|
# Protocol Constants
|
|
13
13
|
# =============================================================================
|
|
14
14
|
|
|
15
15
|
MAGIC: int = 0x004F4C46 # "FLO\0" in little-endian
|
|
16
|
-
VERSION: int =
|
|
16
|
+
VERSION: int = 0x02
|
|
17
|
+
# The hash of the op table this SDK was built for, written into every request
|
|
18
|
+
# header. A server built from another table refuses the request, and its
|
|
19
|
+
# answers carry its own hash, which this SDK refuses. CI's live tests run
|
|
20
|
+
# against the flo commit in .github/flo-ref.env: change both in one commit.
|
|
21
|
+
TABLE_HASH: int = 0x2FBA7DD9AE941830
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Reason(IntEnum):
|
|
25
|
+
"""Why the server refused a request. Mirrors the server's list; the table
|
|
26
|
+
hash covers it, so a server can't send one this SDK doesn't know."""
|
|
27
|
+
|
|
28
|
+
UNKNOWN_OP = 1
|
|
29
|
+
MALFORMED = 2
|
|
30
|
+
FIELD_TOO_LONG = 3
|
|
31
|
+
OUT_OF_RANGE = 4
|
|
32
|
+
LEADER_UNKNOWN = 5
|
|
33
|
+
SHARD_BUSY = 6
|
|
34
|
+
TABLE_MISMATCH = 7
|
|
35
|
+
UNKNOWN_FLAG = 8
|
|
36
|
+
INTERNAL = 9
|
|
37
|
+
COMMITTED_NOT_APPLIED = 10
|
|
38
|
+
ANSWER_LOST = 11
|
|
39
|
+
# A refusal from an op family the server hasn't classified yet.
|
|
40
|
+
UNCLASSIFIED = 12
|
|
41
|
+
# What the request names doesn't exist.
|
|
42
|
+
NOT_FOUND = 13
|
|
43
|
+
# The key is in a prefix the server keeps for itself.
|
|
44
|
+
RESERVED_KEY = 14
|
|
45
|
+
# The write's cas_version isn't the key's version.
|
|
46
|
+
CAS_MISMATCH = 15
|
|
47
|
+
# The write's condition didn't hold.
|
|
48
|
+
CONDITION_FAILED = 16
|
|
49
|
+
# No open transaction has this id here.
|
|
50
|
+
NO_SUCH_TRANSACTION = 17
|
|
51
|
+
# The routing key (the key, when none is given) isn't the one the
|
|
52
|
+
# transaction was begun with.
|
|
53
|
+
WRONG_ROUTING_KEY = 18
|
|
54
|
+
# The transaction holds as many ops or bytes as it may.
|
|
55
|
+
TXN_TOO_LARGE = 19
|
|
56
|
+
# The shard holds as many waiting reads as it may.
|
|
57
|
+
TOO_MANY_WAITERS = 20
|
|
58
|
+
# Two fields given together that don't go together.
|
|
59
|
+
CONFLICTING_FIELDS = 21
|
|
60
|
+
# The shard stopped taking writes until the node restarts.
|
|
61
|
+
WRITES_STOPPED = 22
|
|
62
|
+
# Too many writes wait for commit.
|
|
63
|
+
TOO_MANY_PROPOSALS = 23
|
|
64
|
+
# The key's value isn't what the op works on (a counter, a JSON document).
|
|
65
|
+
WRONG_TYPE = 24
|
|
66
|
+
# The shard holds as many open transactions as it may.
|
|
67
|
+
TOO_MANY_TRANSACTIONS = 25
|
|
68
|
+
# An action complete or fail for a run that has moved on (another worker
|
|
69
|
+
# holds it, or it finished). It was dropped; resending it is refused too.
|
|
70
|
+
STALE_REPORT = 26
|
|
71
|
+
# An await from a worker that missed its heartbeats: a heartbeat (or, once
|
|
72
|
+
# it's removed, registering again) lets it take work again.
|
|
73
|
+
WORKER_UNHEALTHY = 27
|
|
74
|
+
# An await from a draining worker: it takes no new work.
|
|
75
|
+
WORKER_DRAINING = 28
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Ran(IntEnum):
|
|
79
|
+
"""Whether a refused request took effect: retry logic reads this, not the
|
|
80
|
+
message."""
|
|
81
|
+
|
|
82
|
+
NO = 0
|
|
83
|
+
# It took effect; don't resend it.
|
|
84
|
+
YES = 1
|
|
85
|
+
# It may still take effect; check before resending.
|
|
86
|
+
UNKNOWN = 2
|
|
87
|
+
|
|
88
|
+
|
|
17
89
|
HEADER_SIZE: int = 32
|
|
18
90
|
|
|
19
91
|
# Size limits (for client-side validation)
|
|
20
92
|
MAX_NAMESPACE_SIZE: int = 255
|
|
21
93
|
MAX_KEY_SIZE: int = 64 * 1024 # 64 KB
|
|
22
94
|
MAX_VALUE_SIZE: int = 16 * 1024 * 1024 # 16 MB practical limit
|
|
95
|
+
# Longest blocking wait (block_ms) the server accepts; checked client-side so
|
|
96
|
+
# it fails before the round trip.
|
|
97
|
+
MAX_BLOCK_MS: int = 300_000 # 5 minutes
|
|
98
|
+
# Longest a KV get may wait (wait_ms).
|
|
99
|
+
MAX_WAIT_MS: int = 300_000
|
|
100
|
+
# Longest value a KV put takes.
|
|
101
|
+
MAX_KV_VALUE_SIZE: int = 253_817
|
|
102
|
+
# A worker always long-polls: block_ms=0 ("don't wait") would turn its poll
|
|
103
|
+
# loop into a busy spin against the server, so a worker reads 0 (or unset) as this.
|
|
104
|
+
_DEFAULT_WORKER_BLOCK_MS: int = 30_000
|
|
23
105
|
|
|
24
106
|
|
|
25
107
|
# =============================================================================
|
|
@@ -35,11 +117,6 @@ class OpCode(IntEnum):
|
|
|
35
117
|
|
|
36
118
|
# ── System (0x000 – 0x00F) ──
|
|
37
119
|
PING = 0x000
|
|
38
|
-
PONG = 0x001
|
|
39
|
-
ERROR_RESPONSE = 0x002
|
|
40
|
-
AUTH = 0x003
|
|
41
|
-
SET_DURABILITY = 0x004
|
|
42
|
-
OK = 0x005
|
|
43
120
|
|
|
44
121
|
# ── Namespace (0x010 – 0x02F) ──
|
|
45
122
|
NAMESPACE_CREATE = 0x010
|
|
@@ -48,12 +125,6 @@ class OpCode(IntEnum):
|
|
|
48
125
|
NAMESPACE_INFO = 0x013
|
|
49
126
|
NAMESPACE_CONFIG_SET = 0x014
|
|
50
127
|
NAMESPACE_CONFIG_GET = 0x015
|
|
51
|
-
NAMESPACE_CREATE_RESPONSE = 0x020
|
|
52
|
-
NAMESPACE_DELETE_RESPONSE = 0x021
|
|
53
|
-
NAMESPACE_LIST_RESPONSE = 0x022
|
|
54
|
-
NAMESPACE_INFO_RESPONSE = 0x023
|
|
55
|
-
NAMESPACE_CONFIG_SET_RESPONSE = 0x024
|
|
56
|
-
NAMESPACE_CONFIG_GET_RESPONSE = 0x025
|
|
57
128
|
|
|
58
129
|
# ── Cluster (0x030 – 0x04F) ──
|
|
59
130
|
CLUSTER_STATUS = 0x030
|
|
@@ -63,46 +134,34 @@ class OpCode(IntEnum):
|
|
|
63
134
|
CLUSTER_TRANSFER_LEADER = 0x034
|
|
64
135
|
CLUSTER_ADD_NODE = 0x035
|
|
65
136
|
CLUSTER_REMOVE_NODE = 0x036
|
|
66
|
-
CLUSTER_STATUS_RESPONSE = 0x040
|
|
67
|
-
CLUSTER_MEMBERS_RESPONSE = 0x041
|
|
68
|
-
CLUSTER_JOIN_RESPONSE = 0x042
|
|
69
137
|
|
|
70
|
-
# ── KV
|
|
138
|
+
# ── KV (0x100 – 0x12F) ──
|
|
71
139
|
KV_PUT = 0x100
|
|
72
140
|
KV_GET = 0x101
|
|
73
141
|
KV_MGET = 0x102
|
|
74
142
|
KV_DELETE = 0x103
|
|
75
143
|
KV_SCAN = 0x104
|
|
76
144
|
KV_HISTORY = 0x105
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
145
|
+
# KV Extended (atomic counters, JSON ops)
|
|
146
|
+
KV_INCR = 0x10B
|
|
147
|
+
KV_JSON_GET = 0x10C
|
|
148
|
+
KV_JSON_SET = 0x10D
|
|
149
|
+
KV_JSON_DEL = 0x10E
|
|
150
|
+
# KV Per-Shard Transactions
|
|
82
151
|
KV_BEGIN_TXN = 0x110
|
|
83
152
|
KV_COMMIT_TXN = 0x111
|
|
84
153
|
KV_ROLLBACK_TXN = 0x112
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
KV_SNAPSHOT_CREATE_RESPONSE = 0x123
|
|
154
|
+
# KV Extended (TTL, exists)
|
|
155
|
+
KV_EXPIRE = 0x113
|
|
156
|
+
KV_EXISTS = 0x115
|
|
89
157
|
|
|
90
158
|
# ── Streams (0x130 – 0x14F) ──
|
|
91
159
|
STREAM_APPEND = 0x130
|
|
92
160
|
STREAM_READ = 0x131
|
|
93
161
|
STREAM_TRIM = 0x132
|
|
94
162
|
STREAM_INFO = 0x133
|
|
95
|
-
STREAM_APPEND_RESPONSE = 0x134
|
|
96
|
-
STREAM_READ_RESPONSE = 0x135
|
|
97
|
-
STREAM_EVENT = 0x136
|
|
98
|
-
STREAM_SUBSCRIBE = 0x137
|
|
99
|
-
STREAM_UNSUBSCRIBE = 0x138
|
|
100
|
-
STREAM_SUBSCRIBED = 0x139
|
|
101
|
-
STREAM_UNSUBSCRIBED = 0x13A
|
|
102
163
|
STREAM_LIST = 0x13B
|
|
103
|
-
STREAM_LIST_RESPONSE = 0x13C
|
|
104
164
|
STREAM_CREATE = 0x13D
|
|
105
|
-
STREAM_CREATE_RESPONSE = 0x13E
|
|
106
165
|
STREAM_ALTER = 0x13F
|
|
107
166
|
|
|
108
167
|
# ── Stream Consumer Groups (0x150 – 0x16F) ──
|
|
@@ -114,7 +173,6 @@ class OpCode(IntEnum):
|
|
|
114
173
|
STREAM_GROUP_CLAIM = 0x155
|
|
115
174
|
STREAM_GROUP_PENDING = 0x156
|
|
116
175
|
STREAM_GROUP_CONFIGURE_SWEEPER = 0x157
|
|
117
|
-
STREAM_GROUP_READ_RESPONSE = 0x158
|
|
118
176
|
STREAM_GROUP_NACK = 0x159
|
|
119
177
|
STREAM_GROUP_TOUCH = 0x15A
|
|
120
178
|
STREAM_GROUP_INFO = 0x15B
|
|
@@ -124,29 +182,14 @@ class OpCode(IntEnum):
|
|
|
124
182
|
QUEUE_ENQUEUE = 0x170
|
|
125
183
|
QUEUE_DEQUEUE = 0x171
|
|
126
184
|
QUEUE_COMPLETE = 0x172
|
|
127
|
-
QUEUE_EXTEND_LEASE = 0x173
|
|
128
185
|
QUEUE_FAIL = 0x174
|
|
129
|
-
QUEUE_FAIL_AUTO = 0x175
|
|
130
186
|
QUEUE_DLQ_LIST = 0x176
|
|
131
187
|
QUEUE_DLQ_DELETE = 0x177
|
|
132
188
|
QUEUE_DLQ_REQUEUE = 0x178
|
|
133
|
-
QUEUE_DLQ_STATS = 0x179
|
|
134
|
-
QUEUE_PROMOTE_DUE = 0x17A
|
|
135
189
|
QUEUE_STATS = 0x17B
|
|
136
190
|
QUEUE_PEEK = 0x17C
|
|
137
|
-
QUEUE_TOUCH = 0x17D
|
|
138
|
-
QUEUE_BATCH_ENQUEUE = 0x17E
|
|
139
191
|
QUEUE_PURGE = 0x17F
|
|
140
|
-
QUEUE_ENQUEUE_RESPONSE = 0x190
|
|
141
|
-
QUEUE_DEQUEUE_RESPONSE = 0x191
|
|
142
|
-
QUEUE_DLQ_LIST_RESPONSE = 0x192
|
|
143
|
-
QUEUE_STATS_RESPONSE = 0x193
|
|
144
|
-
QUEUE_PEEK_RESPONSE = 0x194
|
|
145
|
-
QUEUE_TOUCH_RESPONSE = 0x195
|
|
146
|
-
QUEUE_BATCH_ENQUEUE_RESPONSE = 0x196
|
|
147
|
-
QUEUE_PURGE_RESPONSE = 0x197
|
|
148
192
|
QUEUE_LIST = 0x198
|
|
149
|
-
QUEUE_LIST_RESPONSE = 0x199
|
|
150
193
|
|
|
151
194
|
# ── Time-Series (0x1A0 – 0x1BF) ──
|
|
152
195
|
TS_WRITE = 0x1A0
|
|
@@ -156,13 +199,6 @@ class OpCode(IntEnum):
|
|
|
156
199
|
TS_LIST = 0x1A4
|
|
157
200
|
TS_DELETE = 0x1A5
|
|
158
201
|
TS_RETENTION = 0x1A6
|
|
159
|
-
TS_WRITE_RESPONSE = 0x1A7
|
|
160
|
-
TS_READ_RESPONSE = 0x1A8
|
|
161
|
-
TS_QUERY_RESPONSE = 0x1A9
|
|
162
|
-
TS_FLOQL_RESPONSE = 0x1AA
|
|
163
|
-
TS_LIST_RESPONSE = 0x1AB
|
|
164
|
-
TS_DELETE_RESPONSE = 0x1AC
|
|
165
|
-
TS_RETENTION_RESPONSE = 0x1AD
|
|
166
202
|
|
|
167
203
|
# ── Actions (0x300 – 0x31F) ──
|
|
168
204
|
ACTION_REGISTER = 0x300
|
|
@@ -175,12 +211,6 @@ class OpCode(IntEnum):
|
|
|
175
211
|
ACTION_COMPLETE = 0x307
|
|
176
212
|
ACTION_FAIL = 0x308
|
|
177
213
|
ACTION_TOUCH = 0x309
|
|
178
|
-
ACTION_REGISTER_RESPONSE = 0x310
|
|
179
|
-
ACTION_INVOKE_RESPONSE = 0x311
|
|
180
|
-
ACTION_STATUS_RESPONSE = 0x312
|
|
181
|
-
ACTION_LIST_RESPONSE = 0x313
|
|
182
|
-
ACTION_LIST_RUNS_RESPONSE = 0x314
|
|
183
|
-
ACTION_TASK_ASSIGNMENT = 0x315
|
|
184
214
|
|
|
185
215
|
# ── Workers (0x320 – 0x33F) ──
|
|
186
216
|
WORKER_REGISTER = 0x320
|
|
@@ -189,10 +219,6 @@ class OpCode(IntEnum):
|
|
|
189
219
|
WORKER_LIST = 0x323
|
|
190
220
|
WORKER_INFO = 0x324
|
|
191
221
|
WORKER_DRAIN = 0x325
|
|
192
|
-
WORKER_REGISTER_RESPONSE = 0x330
|
|
193
|
-
WORKER_LIST_RESPONSE = 0x331
|
|
194
|
-
WORKER_INFO_RESPONSE = 0x332
|
|
195
|
-
WORKER_DRAIN_RESPONSE = 0x333
|
|
196
222
|
|
|
197
223
|
# ── Workflows (0x340 – 0x35F) ──
|
|
198
224
|
WORKFLOW_CREATE = 0x340
|
|
@@ -206,15 +232,6 @@ class OpCode(IntEnum):
|
|
|
206
232
|
WORKFLOW_DISABLE = 0x348
|
|
207
233
|
WORKFLOW_ENABLE = 0x349
|
|
208
234
|
WORKFLOW_LIST_DEFINITIONS = 0x34A
|
|
209
|
-
WORKFLOW_CREATE_RESPONSE = 0x350
|
|
210
|
-
WORKFLOW_START_RESPONSE = 0x351
|
|
211
|
-
WORKFLOW_STATUS_RESPONSE = 0x352
|
|
212
|
-
WORKFLOW_HISTORY_RESPONSE = 0x353
|
|
213
|
-
WORKFLOW_LIST_RUNS_RESPONSE = 0x354
|
|
214
|
-
WORKFLOW_GET_DEFINITION_RESPONSE = 0x355
|
|
215
|
-
WORKFLOW_DISABLE_RESPONSE = 0x356
|
|
216
|
-
WORKFLOW_ENABLE_RESPONSE = 0x357
|
|
217
|
-
WORKFLOW_LIST_DEFINITIONS_RESPONSE = 0x358
|
|
218
235
|
|
|
219
236
|
# ── Processing (0x360 – 0x37F) ──
|
|
220
237
|
PROCESSING_SUBMIT = 0x360
|
|
@@ -222,17 +239,6 @@ class OpCode(IntEnum):
|
|
|
222
239
|
PROCESSING_CANCEL = 0x362
|
|
223
240
|
PROCESSING_STATUS = 0x363
|
|
224
241
|
PROCESSING_LIST = 0x364
|
|
225
|
-
PROCESSING_SAVEPOINT = 0x365
|
|
226
|
-
PROCESSING_RESTORE = 0x366
|
|
227
|
-
PROCESSING_RESCALE = 0x367
|
|
228
|
-
PROCESSING_SUBMIT_RESPONSE = 0x370
|
|
229
|
-
PROCESSING_STOP_RESPONSE = 0x371
|
|
230
|
-
PROCESSING_CANCEL_RESPONSE = 0x372
|
|
231
|
-
PROCESSING_STATUS_RESPONSE = 0x373
|
|
232
|
-
PROCESSING_LIST_RESPONSE = 0x374
|
|
233
|
-
PROCESSING_SAVEPOINT_RESPONSE = 0x375
|
|
234
|
-
PROCESSING_RESTORE_RESPONSE = 0x376
|
|
235
|
-
PROCESSING_RESCALE_RESPONSE = 0x377
|
|
236
242
|
|
|
237
243
|
|
|
238
244
|
class StatusCode(IntEnum):
|
|
@@ -250,6 +256,23 @@ class StatusCode(IntEnum):
|
|
|
250
256
|
INTERNAL_ERROR = 9
|
|
251
257
|
OVERLOADED = 10
|
|
252
258
|
RATE_LIMITED = 11
|
|
259
|
+
UNAVAILABLE = 12
|
|
260
|
+
|
|
261
|
+
@classmethod
|
|
262
|
+
def _missing_(cls, value: object) -> "StatusCode | None":
|
|
263
|
+
# A status this SDK doesn't know still has a body on the wire; failing
|
|
264
|
+
# here would abandon it unread, so it becomes a pseudo-member instead.
|
|
265
|
+
if not isinstance(value, int) or not 0 <= value <= 255:
|
|
266
|
+
return None
|
|
267
|
+
member = int.__new__(cls, value)
|
|
268
|
+
member._name_ = f"UNKNOWN_{value}"
|
|
269
|
+
member._value_ = value
|
|
270
|
+
return member
|
|
271
|
+
|
|
272
|
+
def __reduce_ex__(self, proto: object) -> tuple[type["StatusCode"], tuple[int]]:
|
|
273
|
+
# Rebuilt from its value, not its name: a pseudo-member such as
|
|
274
|
+
# UNKNOWN_200 has no name to look up, so pickle and copy would fail.
|
|
275
|
+
return self.__class__, (int(self),)
|
|
253
276
|
|
|
254
277
|
def message(self) -> str:
|
|
255
278
|
"""Get human-readable error message."""
|
|
@@ -266,8 +289,11 @@ class StatusCode(IntEnum):
|
|
|
266
289
|
StatusCode.INTERNAL_ERROR: "Internal server error",
|
|
267
290
|
StatusCode.OVERLOADED: "Server overloaded",
|
|
268
291
|
StatusCode.RATE_LIMITED: "Request rate limit exceeded",
|
|
292
|
+
StatusCode.UNAVAILABLE: (
|
|
293
|
+
"Unavailable: no leader or the shard isn't taking writes; retry"
|
|
294
|
+
),
|
|
269
295
|
}
|
|
270
|
-
return messages.get(self, "Unknown
|
|
296
|
+
return messages.get(self, f"Unknown status {int(self)}")
|
|
271
297
|
|
|
272
298
|
|
|
273
299
|
class OptionTag(IntEnum):
|
|
@@ -276,26 +302,13 @@ class OptionTag(IntEnum):
|
|
|
276
302
|
Organized by feature area, matching proto.zig definitions.
|
|
277
303
|
"""
|
|
278
304
|
|
|
279
|
-
#
|
|
280
|
-
|
|
281
|
-
CAS_VERSION = 0x02 # u64: Expected version for compare-and-swap
|
|
282
|
-
IF_NOT_EXISTS = 0x03 # void: Only set if key doesn't exist (NX)
|
|
283
|
-
IF_EXISTS = 0x04 # void: Only set if key exists (XX)
|
|
284
|
-
LIMIT = 0x05 # u32: Maximum number of results for scan/list operations
|
|
285
|
-
KEYS_ONLY = 0x06 # u8: Skip values in scan response (0/1)
|
|
286
|
-
CURSOR = 0x07 # bytes: Pagination cursor (ShardWalker format)
|
|
287
|
-
ROUTING_KEY = 0x08 # string: Explicit routing key for shard co-location
|
|
305
|
+
# General Options (0x01 - 0x0F)
|
|
306
|
+
LIMIT = 0x05 # u32: Maximum number of results for list operations
|
|
288
307
|
|
|
289
308
|
# Queue Options (0x10 - 0x1F)
|
|
290
|
-
PRIORITY = 0x10 # u8: Message priority (0-255,
|
|
291
|
-
DELAY_MS = 0x11 # u64: Delay before message becomes visible
|
|
292
|
-
VISIBILITY_TIMEOUT_MS = 0x12 # u32: How long message is invisible after dequeue
|
|
293
|
-
DEDUP_KEY = 0x13 # string: Deduplication key
|
|
294
|
-
MAX_RETRIES = 0x14 # u8: Maximum retry attempts before DLQ
|
|
309
|
+
PRIORITY = 0x10 # u8: Message priority (0-255, lower is taken first)
|
|
295
310
|
COUNT = 0x15 # u32: Number of messages to dequeue
|
|
296
|
-
|
|
297
|
-
BLOCK_MS = 0x17 # u32: Block timeout - wait until exists (0=forever)
|
|
298
|
-
WAIT_MS = 0x18 # u32: Watch timeout - wait for NEXT version change (0=forever)
|
|
311
|
+
BLOCK_MS = 0x17 # u32: Block timeout - wait for data (0=don't wait, max 300000)
|
|
299
312
|
|
|
300
313
|
# Stream Options (0x20 - 0x2F) - StreamID-native ONLY
|
|
301
314
|
# All stream positioning uses StreamID (timestamp_ms + sequence)
|
|
@@ -306,36 +319,11 @@ class OptionTag(IntEnum):
|
|
|
306
319
|
PARTITION = 0x24 # u32: Explicit partition index
|
|
307
320
|
PARTITION_KEY = 0x25 # string: Key for partition routing
|
|
308
321
|
MAX_AGE_SECONDS = 0x26 # u64: Maximum age in seconds for retention
|
|
309
|
-
MAX_BYTES = 0x27 # u64: Maximum size in bytes for retention
|
|
310
322
|
DRY_RUN = 0x28 # void: Flag to preview what would be deleted
|
|
311
|
-
RETENTION_COUNT = 0x29 # u64: Retention policy - max event count
|
|
312
|
-
RETENTION_AGE = 0x2A # u64: Retention policy - max age in seconds
|
|
313
|
-
RETENTION_BYTES = 0x2B # u64: Retention policy - max bytes
|
|
314
323
|
|
|
315
324
|
# Consumer Group Options (0x30 - 0x3F)
|
|
316
325
|
ACK_TIMEOUT_MS = 0x30 # u32: Time before unacked message auto-redelivers
|
|
317
326
|
MAX_DELIVER = 0x31 # u8: Max delivery attempts before DLQ (default: 10)
|
|
318
|
-
SUBSCRIPTION_MODE = 0x32 # u8: 0=shared, 1=exclusive, 2=key_shared
|
|
319
|
-
REDELIVERY_DELAY_MS = 0x33 # u32: Delay before NACK'd message becomes visible
|
|
320
|
-
CONSUMER_TIMEOUT_MS = 0x34 # u32: Remove consumer from group if no activity
|
|
321
|
-
NO_ACK = 0x35 # void: Auto-ack on delivery (at-most-once)
|
|
322
|
-
IDLE_TIMEOUT_MS = 0x36 # u64: Min idle time for claiming stuck messages
|
|
323
|
-
MAX_ACK_PENDING = 0x37 # u32: Max unacked messages per consumer
|
|
324
|
-
EXTEND_ACK_MS = 0x38 # u32: Amount of time to extend ack deadline
|
|
325
|
-
MAX_STANDBYS = 0x39 # u16: Max standby consumers in exclusive mode
|
|
326
|
-
NUM_SLOTS = 0x3A # u16: Number of hash slots for key_shared mode
|
|
327
|
-
|
|
328
|
-
# Worker/Action Options (0x40 - 0x4F)
|
|
329
|
-
WORKER_ID = 0x40 # string: Worker identifier
|
|
330
|
-
EXTEND_MS = 0x41 # u32: Lease extension time in milliseconds
|
|
331
|
-
MAX_TASKS = 0x42 # u32: Maximum tasks to return in batch
|
|
332
|
-
RETRY = 0x43 # u8: Whether to retry on failure (0/1)
|
|
333
|
-
|
|
334
|
-
# Workflow Options (0x50 - 0x5F)
|
|
335
|
-
TIMEOUT_MS = 0x50 # u64: Workflow/activity timeout
|
|
336
|
-
RETRY_POLICY = 0x51 # bytes: Serialized retry policy
|
|
337
|
-
CORRELATION_ID = 0x52 # string: Correlation ID for tracing
|
|
338
|
-
SUBSCRIPTION_ID = 0x53 # u64: Subscription ID for stream subscriptions
|
|
339
327
|
|
|
340
328
|
# Time-Series Options (0x60 - 0x6F)
|
|
341
329
|
TS_FROM_MS = 0x60 # i64: Start of time range (inclusive, unix ms)
|
|
@@ -344,11 +332,9 @@ class OptionTag(IntEnum):
|
|
|
344
332
|
TS_AGGREGATION = 0x63 # string: Aggregation function name (avg, sum, count, min, max)
|
|
345
333
|
TS_FIELD = 0x64 # string: Field name filter (empty = "value")
|
|
346
334
|
TS_TAGS = 0x65 # string: Comma-separated tag filters "key=val,key2=val2"
|
|
347
|
-
TS_PRECISION = 0x66 # u8: Timestamp precision (0=ns, 1=us, 2=ms, 3=s)
|
|
348
335
|
TS_TIMESTAMP = 0x67 # i64: Explicit timestamp for write (0 = server-assigned)
|
|
349
336
|
TS_RAW_TTL = 0x68 # string: Raw data TTL (e.g., "7d")
|
|
350
337
|
TS_DOWNSAMPLE = 0x69 # string: Downsample rule (e.g., "1m:avg:30d")
|
|
351
|
-
TS_BATCH = 0x6A # void: Flag indicating batch/line-protocol mode
|
|
352
338
|
|
|
353
339
|
|
|
354
340
|
# =============================================================================
|
|
@@ -357,29 +343,114 @@ class OptionTag(IntEnum):
|
|
|
357
343
|
|
|
358
344
|
|
|
359
345
|
@dataclass
|
|
360
|
-
class
|
|
361
|
-
"""
|
|
346
|
+
class ScanResult:
|
|
347
|
+
"""A page of a KV scan: the keys' names, and the cursor for the next page."""
|
|
362
348
|
|
|
363
|
-
|
|
364
|
-
|
|
349
|
+
names: list[str]
|
|
350
|
+
cursor: bytes | None # None on the last page
|
|
351
|
+
|
|
352
|
+
@property
|
|
353
|
+
def has_more(self) -> bool:
|
|
354
|
+
return self.cursor is not None
|
|
365
355
|
|
|
366
356
|
|
|
367
357
|
@dataclass
|
|
368
|
-
class
|
|
369
|
-
"""
|
|
358
|
+
class VersionEntry:
|
|
359
|
+
"""One version of a key from KV history."""
|
|
370
360
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
361
|
+
version: int
|
|
362
|
+
at_ms: int # When it was written, unix ms
|
|
363
|
+
value: bytes
|
|
374
364
|
|
|
375
365
|
|
|
376
366
|
@dataclass
|
|
377
|
-
class
|
|
378
|
-
"""KV
|
|
367
|
+
class IncrResult:
|
|
368
|
+
"""A KV incr's new counter value and the key's new version."""
|
|
369
|
+
|
|
370
|
+
value: int
|
|
371
|
+
version: int
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
@dataclass
|
|
375
|
+
class PutResult:
|
|
376
|
+
"""Result of a successful KV put.
|
|
377
|
+
|
|
378
|
+
The ``version`` field is the new version assigned by the server, suitable
|
|
379
|
+
for CAS on the next write via :class:`PutOptions.cas_version`.
|
|
380
|
+
"""
|
|
381
|
+
|
|
382
|
+
version: int
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
@dataclass
|
|
386
|
+
class KVBeginResult:
|
|
387
|
+
"""Result of a successful KV transaction begin.
|
|
388
|
+
|
|
389
|
+
``txn_id`` is the server-assigned transaction handle. ``pinned_hash`` is
|
|
390
|
+
the partition hash this transaction is bound to — every key written or
|
|
391
|
+
read inside the transaction must hash to the same partition.
|
|
392
|
+
"""
|
|
393
|
+
|
|
394
|
+
txn_id: int
|
|
395
|
+
pinned_hash: int
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
@dataclass
|
|
399
|
+
class KVCommitResult:
|
|
400
|
+
"""Result of a successful KV transaction commit.
|
|
401
|
+
|
|
402
|
+
``commit_index`` is the Raft log index of the committed batch and
|
|
403
|
+
``op_count`` is the number of buffered operations applied atomically.
|
|
404
|
+
"""
|
|
405
|
+
|
|
406
|
+
commit_index: int
|
|
407
|
+
op_count: int
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
@dataclass
|
|
411
|
+
class GetResult:
|
|
412
|
+
"""Result of a KV get that found a key.
|
|
413
|
+
|
|
414
|
+
``kv.get`` returns ``None`` when the key is missing; check for ``None``
|
|
415
|
+
before dereferencing.
|
|
416
|
+
"""
|
|
379
417
|
|
|
418
|
+
value: bytes
|
|
380
419
|
version: int
|
|
381
|
-
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
T = TypeVar("T")
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
@dataclass
|
|
426
|
+
class GetJsonResult(Generic[T]):
|
|
427
|
+
"""Result of a :meth:`KV.json_get` that found the key and path.
|
|
428
|
+
|
|
429
|
+
Unlike :class:`GetResult`, ``value`` is the already-parsed JSON (the
|
|
430
|
+
``KV_JSON_GET`` wire body is always a JSON document, so it is decoded for
|
|
431
|
+
you). ``version`` is the document's current version. ``kv.json_get``
|
|
432
|
+
returns ``None`` when the key or path is missing.
|
|
433
|
+
|
|
434
|
+
The value type defaults to ``Any``; the SDK does not validate the shape at
|
|
435
|
+
runtime.
|
|
436
|
+
"""
|
|
437
|
+
|
|
438
|
+
value: Any
|
|
439
|
+
version: int
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
@dataclass
|
|
443
|
+
class MGetEntry:
|
|
444
|
+
"""One entry in a :meth:`KV.mget` response.
|
|
445
|
+
|
|
446
|
+
``found`` is ``False`` when the key did not exist; in that case ``value``
|
|
447
|
+
is ``b''`` and ``version`` is ``0``.
|
|
448
|
+
"""
|
|
449
|
+
|
|
450
|
+
key: str
|
|
382
451
|
value: bytes
|
|
452
|
+
version: int
|
|
453
|
+
found: bool
|
|
383
454
|
|
|
384
455
|
|
|
385
456
|
@dataclass
|
|
@@ -388,6 +459,9 @@ class Message:
|
|
|
388
459
|
|
|
389
460
|
seq: int
|
|
390
461
|
payload: bytes
|
|
462
|
+
enqueued_at_ms: int = 0
|
|
463
|
+
delivery_count: int = 0
|
|
464
|
+
priority: int = 0
|
|
391
465
|
|
|
392
466
|
|
|
393
467
|
@dataclass
|
|
@@ -426,12 +500,10 @@ class StreamID:
|
|
|
426
500
|
|
|
427
501
|
|
|
428
502
|
class StorageTier(IntEnum):
|
|
429
|
-
"""
|
|
503
|
+
"""The tier a stream record was served from; the server sends only these two."""
|
|
430
504
|
|
|
431
|
-
HOT = 0
|
|
432
|
-
|
|
433
|
-
WARM = 2
|
|
434
|
-
COLD = 3
|
|
505
|
+
HOT = 0 # the in-memory log
|
|
506
|
+
WARM = 1 # the warm store, after the record left the log
|
|
435
507
|
|
|
436
508
|
|
|
437
509
|
@dataclass
|
|
@@ -440,6 +512,7 @@ class StreamRecord:
|
|
|
440
512
|
|
|
441
513
|
id: StreamID = None # type: ignore[assignment]
|
|
442
514
|
tier: StorageTier = StorageTier.HOT
|
|
515
|
+
stream: str = ""
|
|
443
516
|
payload: bytes = b""
|
|
444
517
|
headers: dict[str, str] | None = None
|
|
445
518
|
|
|
@@ -458,6 +531,35 @@ class StreamReadResult:
|
|
|
458
531
|
records: list[StreamRecord]
|
|
459
532
|
|
|
460
533
|
|
|
534
|
+
@dataclass
|
|
535
|
+
class PendingEntry:
|
|
536
|
+
"""One entry in a consumer group's Pending Entry List (PEL).
|
|
537
|
+
|
|
538
|
+
A delivered-but-unacked message. ``delivery_count`` is how many times the
|
|
539
|
+
entry has been delivered; ``consumer`` is the consumer that currently owns
|
|
540
|
+
it.
|
|
541
|
+
"""
|
|
542
|
+
|
|
543
|
+
id: StreamID = None # type: ignore[assignment]
|
|
544
|
+
consumer: str = ""
|
|
545
|
+
delivery_count: int = 0
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
@dataclass
|
|
549
|
+
class StreamClaimResult:
|
|
550
|
+
"""Result of a :meth:`StreamOperations.group_claim` cursor page.
|
|
551
|
+
|
|
552
|
+
``records`` carry payload + headers (same shape as ``group_read``).
|
|
553
|
+
``next_cursor`` is the ``start_id`` to pass on the next ``group_claim``
|
|
554
|
+
call; when ``done`` is ``True`` the PEL has been fully scanned and the loop
|
|
555
|
+
should stop.
|
|
556
|
+
"""
|
|
557
|
+
|
|
558
|
+
records: list[StreamRecord]
|
|
559
|
+
next_cursor: StreamID = None # type: ignore[assignment]
|
|
560
|
+
done: bool = False
|
|
561
|
+
|
|
562
|
+
|
|
461
563
|
@dataclass
|
|
462
564
|
class StreamInfo:
|
|
463
565
|
"""Stream metadata."""
|
|
@@ -474,12 +576,34 @@ class StreamInfo:
|
|
|
474
576
|
# =============================================================================
|
|
475
577
|
|
|
476
578
|
|
|
579
|
+
class WaitFor(IntEnum):
|
|
580
|
+
"""What a KV get with wait_ms waits for."""
|
|
581
|
+
|
|
582
|
+
# The key to exist; one that exists answers at once.
|
|
583
|
+
EXISTS = 0
|
|
584
|
+
# The key's next version.
|
|
585
|
+
CHANGE = 1
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
class Condition(IntEnum):
|
|
589
|
+
"""When a KV put may write."""
|
|
590
|
+
|
|
591
|
+
ALWAYS = 0
|
|
592
|
+
IF_ABSENT = 1
|
|
593
|
+
IF_PRESENT = 2
|
|
594
|
+
|
|
595
|
+
|
|
477
596
|
@dataclass
|
|
478
597
|
class GetOptions:
|
|
479
598
|
"""Options for KV get operations."""
|
|
480
599
|
|
|
481
600
|
namespace: str | None = None
|
|
482
|
-
|
|
601
|
+
# Wait up to this long for what wait_for names (0 = don't wait, max 300000).
|
|
602
|
+
wait_ms: int = 0
|
|
603
|
+
# The key to exist (the default), or its next version.
|
|
604
|
+
wait_for: WaitFor = WaitFor.EXISTS
|
|
605
|
+
# Routes the request in place of the key.
|
|
606
|
+
routing_key: str | None = None
|
|
483
607
|
|
|
484
608
|
|
|
485
609
|
@dataclass
|
|
@@ -487,10 +611,13 @@ class PutOptions:
|
|
|
487
611
|
"""Options for KV put operations."""
|
|
488
612
|
|
|
489
613
|
namespace: str | None = None
|
|
490
|
-
|
|
614
|
+
ttl_ms: int | None = None # Time-to-live in milliseconds (0 = no expiration)
|
|
615
|
+
# When the write may happen.
|
|
616
|
+
condition: Condition = Condition.ALWAYS
|
|
617
|
+
# The key's version the write requires; 0 means it doesn't exist.
|
|
491
618
|
cas_version: int | None = None
|
|
492
|
-
|
|
493
|
-
|
|
619
|
+
# Routes the request in place of the key.
|
|
620
|
+
routing_key: str | None = None
|
|
494
621
|
|
|
495
622
|
|
|
496
623
|
@dataclass
|
|
@@ -498,6 +625,10 @@ class DeleteOptions:
|
|
|
498
625
|
"""Options for KV delete operations."""
|
|
499
626
|
|
|
500
627
|
namespace: str | None = None
|
|
628
|
+
# Only delete when the key's version is this.
|
|
629
|
+
cas_version: int | None = None
|
|
630
|
+
# Routes the request in place of the key.
|
|
631
|
+
routing_key: str | None = None
|
|
501
632
|
|
|
502
633
|
|
|
503
634
|
@dataclass
|
|
@@ -505,9 +636,9 @@ class ScanOptions:
|
|
|
505
636
|
"""Options for KV scan operations."""
|
|
506
637
|
|
|
507
638
|
namespace: str | None = None
|
|
639
|
+
# The cursor from the previous page; None for the first.
|
|
508
640
|
cursor: bytes | None = None
|
|
509
|
-
limit: int
|
|
510
|
-
keys_only: bool = False
|
|
641
|
+
limit: int = 1024 # 1..1024 names per page
|
|
511
642
|
|
|
512
643
|
|
|
513
644
|
@dataclass
|
|
@@ -515,7 +646,58 @@ class HistoryOptions:
|
|
|
515
646
|
"""Options for KV history operations."""
|
|
516
647
|
|
|
517
648
|
namespace: str | None = None
|
|
518
|
-
limit: int
|
|
649
|
+
limit: int = 10 # 1..65 versions, newest first
|
|
650
|
+
# Routes the request in place of the key.
|
|
651
|
+
routing_key: str | None = None
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
@dataclass
|
|
655
|
+
class KVIncrOptions:
|
|
656
|
+
"""Options for KV incr operations."""
|
|
657
|
+
|
|
658
|
+
namespace: str | None = None
|
|
659
|
+
delta: int = 1
|
|
660
|
+
# Routes the request in place of the key.
|
|
661
|
+
routing_key: str | None = None
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
@dataclass
|
|
665
|
+
class KVExpireOptions:
|
|
666
|
+
"""Options for KV expire operations."""
|
|
667
|
+
|
|
668
|
+
namespace: str | None = None
|
|
669
|
+
# Only succeed when the key's version is this.
|
|
670
|
+
cas_version: int | None = None
|
|
671
|
+
# Routes the request in place of the key.
|
|
672
|
+
routing_key: str | None = None
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
@dataclass
|
|
676
|
+
class KVExistsOptions:
|
|
677
|
+
"""Options for KV exists operations."""
|
|
678
|
+
|
|
679
|
+
namespace: str | None = None
|
|
680
|
+
# Routes the request in place of the key.
|
|
681
|
+
routing_key: str | None = None
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
@dataclass
|
|
685
|
+
class KVJsonOptions:
|
|
686
|
+
"""Options for KV JSON.* operations."""
|
|
687
|
+
|
|
688
|
+
namespace: str | None = None
|
|
689
|
+
# Routes the request in place of the key.
|
|
690
|
+
routing_key: str | None = None
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
@dataclass
|
|
694
|
+
class KVMGetOptions:
|
|
695
|
+
"""Options for KV mget operations."""
|
|
696
|
+
|
|
697
|
+
namespace: str | None = None
|
|
698
|
+
# The routing key every key was written with: all are looked up on its
|
|
699
|
+
# shard. Without it, each key is looked up on its own.
|
|
700
|
+
routing_key: str | None = None
|
|
519
701
|
|
|
520
702
|
|
|
521
703
|
@dataclass
|
|
@@ -523,9 +705,7 @@ class EnqueueOptions:
|
|
|
523
705
|
"""Options for queue enqueue operations."""
|
|
524
706
|
|
|
525
707
|
namespace: str | None = None
|
|
526
|
-
priority: int = 0
|
|
527
|
-
delay_ms: int | None = None
|
|
528
|
-
dedup_key: str | None = None
|
|
708
|
+
priority: int = 0 # 0-255, lower is taken first
|
|
529
709
|
|
|
530
710
|
|
|
531
711
|
@dataclass
|
|
@@ -533,8 +713,7 @@ class DequeueOptions:
|
|
|
533
713
|
"""Options for queue dequeue operations."""
|
|
534
714
|
|
|
535
715
|
namespace: str | None = None
|
|
536
|
-
|
|
537
|
-
block_ms: int | None = None
|
|
716
|
+
block_ms: int | None = None # Wait for messages (0 = don't wait, max 300000)
|
|
538
717
|
|
|
539
718
|
|
|
540
719
|
@dataclass
|
|
@@ -549,7 +728,6 @@ class NackOptions:
|
|
|
549
728
|
"""Options for queue nack operations."""
|
|
550
729
|
|
|
551
730
|
namespace: str | None = None
|
|
552
|
-
to_dlq: bool = False
|
|
553
731
|
|
|
554
732
|
|
|
555
733
|
@dataclass
|
|
@@ -557,7 +735,6 @@ class DlqListOptions:
|
|
|
557
735
|
"""Options for DLQ list operations."""
|
|
558
736
|
|
|
559
737
|
namespace: str | None = None
|
|
560
|
-
limit: int = 100
|
|
561
738
|
|
|
562
739
|
|
|
563
740
|
@dataclass
|
|
@@ -574,13 +751,6 @@ class PeekOptions:
|
|
|
574
751
|
namespace: str | None = None
|
|
575
752
|
|
|
576
753
|
|
|
577
|
-
@dataclass
|
|
578
|
-
class TouchOptions:
|
|
579
|
-
"""Options for queue touch (lease renewal) operations."""
|
|
580
|
-
|
|
581
|
-
namespace: str | None = None
|
|
582
|
-
|
|
583
|
-
|
|
584
754
|
# =============================================================================
|
|
585
755
|
# Stream Option Types
|
|
586
756
|
# =============================================================================
|
|
@@ -607,7 +777,7 @@ class StreamReadOptions:
|
|
|
607
777
|
tail: bool = False # Start from end of stream (mutually exclusive with start)
|
|
608
778
|
partition: int | None = None # Explicit partition index
|
|
609
779
|
count: int | None = None # Maximum number of records to return
|
|
610
|
-
block_ms: int | None = None # Blocking timeout (0 =
|
|
780
|
+
block_ms: int | None = None # Blocking timeout (0 = don't wait, max 300000)
|
|
611
781
|
|
|
612
782
|
|
|
613
783
|
@dataclass
|
|
@@ -615,10 +785,18 @@ class StreamTrimOptions:
|
|
|
615
785
|
"""Options for stream trim operations."""
|
|
616
786
|
|
|
617
787
|
namespace: str | None = None
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
dry_run: bool = False #
|
|
788
|
+
before: Optional["StreamID"] = None # Remove records up to and including this id
|
|
789
|
+
max_len: int | None = None # Keep only the newest N records (> 0)
|
|
790
|
+
max_age_seconds: int | None = None # Remove records older than this (> 0)
|
|
791
|
+
dry_run: bool = False # Report what would be removed, removing nothing
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
@dataclass
|
|
795
|
+
class StreamTrimResult:
|
|
796
|
+
"""What a trim removed, or with dry_run would remove."""
|
|
797
|
+
|
|
798
|
+
removed: int # Records removed
|
|
799
|
+
first_seq: int # Sequence of the first record left; meaningful only when records remain
|
|
622
800
|
|
|
623
801
|
|
|
624
802
|
@dataclass
|
|
@@ -641,7 +819,7 @@ class StreamGroupReadOptions:
|
|
|
641
819
|
|
|
642
820
|
namespace: str | None = None
|
|
643
821
|
count: int | None = None # Max records to read
|
|
644
|
-
block_ms: int | None = None # Block waiting for records
|
|
822
|
+
block_ms: int | None = None # Block waiting for records (0 = don't wait, max 300000)
|
|
645
823
|
|
|
646
824
|
|
|
647
825
|
@dataclass
|
|
@@ -649,7 +827,7 @@ class StreamGroupAckOptions:
|
|
|
649
827
|
"""Options for acknowledging records in a consumer group."""
|
|
650
828
|
|
|
651
829
|
namespace: str | None = None
|
|
652
|
-
consumer: str = "" #
|
|
830
|
+
consumer: str = "" # Sent, but the server doesn't check it: any consumer can ack an entry
|
|
653
831
|
|
|
654
832
|
|
|
655
833
|
@dataclass
|
|
@@ -657,8 +835,7 @@ class StreamGroupNackOptions:
|
|
|
657
835
|
"""Options for negatively acknowledging records in a consumer group."""
|
|
658
836
|
|
|
659
837
|
namespace: str | None = None
|
|
660
|
-
consumer: str = "" #
|
|
661
|
-
redelivery_delay_ms: int | None = None # Delay before message becomes visible again
|
|
838
|
+
consumer: str = "" # Sent, but the server doesn't check it: any consumer can nack an entry
|
|
662
839
|
|
|
663
840
|
|
|
664
841
|
# =============================================================================
|
|
@@ -683,14 +860,34 @@ class ActionInfo:
|
|
|
683
860
|
description: str | None = None
|
|
684
861
|
|
|
685
862
|
|
|
863
|
+
class RunStatus(IntEnum):
|
|
864
|
+
"""An action run's status."""
|
|
865
|
+
|
|
866
|
+
PENDING = 0
|
|
867
|
+
RUNNING = 1
|
|
868
|
+
COMPLETED = 2
|
|
869
|
+
FAILED = 3
|
|
870
|
+
CANCELLED = 4
|
|
871
|
+
TIMED_OUT = 5
|
|
872
|
+
|
|
873
|
+
|
|
686
874
|
@dataclass
|
|
687
875
|
class ActionRunStatus:
|
|
688
|
-
"""Status of an action run.
|
|
876
|
+
"""Status of an action run. Times are ms since the epoch; ``attempt``
|
|
877
|
+
counts from 1 once a worker has taken the run. ``output`` is a completed
|
|
878
|
+
run's result, and ``error_message`` the last failure's message."""
|
|
689
879
|
|
|
690
880
|
run_id: str
|
|
691
|
-
status:
|
|
692
|
-
|
|
693
|
-
|
|
881
|
+
status: RunStatus
|
|
882
|
+
created_at_ms: int
|
|
883
|
+
attempt: int
|
|
884
|
+
started_at_ms: int | None = None
|
|
885
|
+
completed_at_ms: int | None = None
|
|
886
|
+
output: bytes | None = None
|
|
887
|
+
error_message: str | None = None
|
|
888
|
+
# The worker holding the run, or that last held it; None until one takes
|
|
889
|
+
# it. Only the leader learns of a take at once.
|
|
890
|
+
worker_id: str | None = None
|
|
694
891
|
|
|
695
892
|
|
|
696
893
|
@dataclass
|
|
@@ -715,12 +912,23 @@ class ActionListResult:
|
|
|
715
912
|
|
|
716
913
|
@dataclass
|
|
717
914
|
class ActionRegisterOptions:
|
|
718
|
-
"""Options for registering an action.
|
|
915
|
+
"""Options for registering an action. Registering an action replaces
|
|
916
|
+
the settings it had: the last registration wins."""
|
|
719
917
|
|
|
720
918
|
namespace: str | None = None
|
|
919
|
+
# How long a run may go without its worker completing, failing or
|
|
920
|
+
# touching it before it's taken back; 0 means never.
|
|
721
921
|
timeout_ms: int = 30000
|
|
922
|
+
# Attempts in all, counting the first.
|
|
722
923
|
max_retries: int = 3
|
|
723
|
-
|
|
924
|
+
owner: str | None = None
|
|
925
|
+
|
|
926
|
+
def __post_init__(self) -> None:
|
|
927
|
+
# Both go on the wire as u32.
|
|
928
|
+
for name in ("timeout_ms", "max_retries"):
|
|
929
|
+
value = getattr(self, name)
|
|
930
|
+
if not 0 <= value < 2**32:
|
|
931
|
+
raise ValueError(f"{name} must be in [0, 2**32), not {value}")
|
|
724
932
|
|
|
725
933
|
|
|
726
934
|
@dataclass
|
|
@@ -728,8 +936,9 @@ class ActionInvokeOptions:
|
|
|
728
936
|
"""Options for invoking an action."""
|
|
729
937
|
|
|
730
938
|
namespace: str | None = None
|
|
731
|
-
|
|
732
|
-
|
|
939
|
+
# Required worker labels: only workers registered with every key/value
|
|
940
|
+
# here receive the run.
|
|
941
|
+
labels: dict[str, Any] | None = None
|
|
733
942
|
|
|
734
943
|
|
|
735
944
|
@dataclass
|
|
@@ -769,10 +978,8 @@ class TaskAssignment:
|
|
|
769
978
|
payload: bytes
|
|
770
979
|
created_at: int
|
|
771
980
|
attempt: int
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
# Alias for backwards compatibility
|
|
775
|
-
WorkerTask = TaskAssignment
|
|
981
|
+
caller_run_id: str = ""
|
|
982
|
+
caller_workflow_name: str = ""
|
|
776
983
|
|
|
777
984
|
|
|
778
985
|
@dataclass
|
|
@@ -782,19 +989,44 @@ class WorkerAwaitResult:
|
|
|
782
989
|
task: TaskAssignment | None = None # None if no task available
|
|
783
990
|
|
|
784
991
|
|
|
992
|
+
class WorkerType(IntEnum):
|
|
993
|
+
"""What a worker runs."""
|
|
994
|
+
|
|
995
|
+
ACTION = 0
|
|
996
|
+
STREAM = 1
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
class WorkerStatus(IntEnum):
|
|
1000
|
+
"""A registered worker's status."""
|
|
1001
|
+
|
|
1002
|
+
ACTIVE = 0
|
|
1003
|
+
IDLE = 1
|
|
1004
|
+
DRAINING = 2
|
|
1005
|
+
UNHEALTHY = 3
|
|
1006
|
+
|
|
1007
|
+
|
|
785
1008
|
@dataclass
|
|
786
1009
|
class WorkerInfo:
|
|
787
|
-
"""
|
|
1010
|
+
"""A registered worker, as worker.list reports it. Times are ms since the epoch."""
|
|
788
1011
|
|
|
789
1012
|
worker_id: str
|
|
790
|
-
|
|
1013
|
+
worker_type: WorkerType
|
|
1014
|
+
status: WorkerStatus
|
|
1015
|
+
current_load: int
|
|
1016
|
+
max_concurrency: int
|
|
1017
|
+
tasks_completed: int
|
|
1018
|
+
tasks_failed: int
|
|
1019
|
+
registered_at_ms: int
|
|
1020
|
+
last_heartbeat_ms: int
|
|
1021
|
+
machine_id: str | None = None
|
|
791
1022
|
|
|
792
1023
|
|
|
793
1024
|
@dataclass
|
|
794
1025
|
class WorkerListResult:
|
|
795
|
-
"""
|
|
1026
|
+
"""A page of workers, in id order. ``cursor`` gives the next page; None on the last."""
|
|
796
1027
|
|
|
797
1028
|
workers: list[WorkerInfo]
|
|
1029
|
+
cursor: bytes | None = None
|
|
798
1030
|
|
|
799
1031
|
|
|
800
1032
|
# =============================================================================
|
|
@@ -817,8 +1049,7 @@ class WorkerAwaitOptions:
|
|
|
817
1049
|
"""Options for awaiting a task."""
|
|
818
1050
|
|
|
819
1051
|
namespace: str | None = None
|
|
820
|
-
block_ms: int | None = None # Block waiting for task (0 =
|
|
821
|
-
timeout_ms: int | None = None
|
|
1052
|
+
block_ms: int | None = None # Block waiting for task (None = 30000, 0 = don't wait, max 300000)
|
|
822
1053
|
|
|
823
1054
|
|
|
824
1055
|
@dataclass
|
|
@@ -847,10 +1078,12 @@ class WorkerFailOptions:
|
|
|
847
1078
|
|
|
848
1079
|
@dataclass
|
|
849
1080
|
class WorkerListOptions:
|
|
850
|
-
"""Options for listing workers
|
|
1081
|
+
"""Options for listing workers: at most ``limit`` (1..1024) after ``cursor``,
|
|
1082
|
+
the last page's cursor (None starts)."""
|
|
851
1083
|
|
|
852
1084
|
namespace: str | None = None
|
|
853
1085
|
limit: int = 100
|
|
1086
|
+
cursor: bytes | None = None
|
|
854
1087
|
|
|
855
1088
|
|
|
856
1089
|
# =============================================================================
|
|
@@ -1005,27 +1238,6 @@ class ProcessingCancelOptions:
|
|
|
1005
1238
|
namespace: str | None = None
|
|
1006
1239
|
|
|
1007
1240
|
|
|
1008
|
-
@dataclass
|
|
1009
|
-
class ProcessingSavepointOptions:
|
|
1010
|
-
"""Options for triggering a savepoint."""
|
|
1011
|
-
|
|
1012
|
-
namespace: str | None = None
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
@dataclass
|
|
1016
|
-
class ProcessingRestoreOptions:
|
|
1017
|
-
"""Options for restoring from a savepoint."""
|
|
1018
|
-
|
|
1019
|
-
namespace: str | None = None
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
@dataclass
|
|
1023
|
-
class ProcessingRescaleOptions:
|
|
1024
|
-
"""Options for rescaling a processing job."""
|
|
1025
|
-
|
|
1026
|
-
namespace: str | None = None
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
1241
|
@dataclass
|
|
1030
1242
|
class ProcessingSyncOptions:
|
|
1031
1243
|
"""Options for declarative processing sync."""
|
|
@@ -1040,7 +1252,6 @@ class ProcessingStatusResult:
|
|
|
1040
1252
|
job_id: str
|
|
1041
1253
|
name: str
|
|
1042
1254
|
status: str
|
|
1043
|
-
parallelism: int
|
|
1044
1255
|
batch_size: int
|
|
1045
1256
|
records_processed: int
|
|
1046
1257
|
created_at: int
|
|
@@ -1053,7 +1264,6 @@ class ProcessingListEntry:
|
|
|
1053
1264
|
name: str
|
|
1054
1265
|
job_id: str
|
|
1055
1266
|
status: str
|
|
1056
|
-
parallelism: int
|
|
1057
1267
|
created_at: int
|
|
1058
1268
|
|
|
1059
1269
|
|