flwr 1.15.2__py3-none-any.whl → 1.16.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.
- flwr/cli/build.py +2 -0
- flwr/cli/log.py +20 -21
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- flwr/client/client_app.py +147 -36
- flwr/client/clientapp/app.py +4 -0
- flwr/client/message_handler/message_handler.py +1 -1
- flwr/client/rest_client/connection.py +4 -6
- flwr/client/supernode/__init__.py +0 -2
- flwr/client/supernode/app.py +1 -11
- flwr/common/address.py +35 -0
- flwr/common/args.py +8 -2
- flwr/common/auth_plugin/auth_plugin.py +2 -1
- flwr/common/constant.py +16 -0
- flwr/common/event_log_plugin/__init__.py +22 -0
- flwr/common/event_log_plugin/event_log_plugin.py +60 -0
- flwr/common/grpc.py +1 -1
- flwr/common/message.py +18 -7
- flwr/common/object_ref.py +0 -10
- flwr/common/record/conversion_utils.py +8 -17
- flwr/common/record/parametersrecord.py +151 -16
- flwr/common/record/recordset.py +95 -88
- flwr/common/secure_aggregation/quantization.py +5 -1
- flwr/common/serde.py +8 -126
- flwr/common/telemetry.py +0 -10
- flwr/common/typing.py +36 -0
- flwr/server/app.py +18 -2
- flwr/server/compat/app.py +4 -1
- flwr/server/compat/app_utils.py +10 -2
- flwr/server/compat/driver_client_proxy.py +2 -2
- flwr/server/driver/driver.py +1 -1
- flwr/server/driver/grpc_driver.py +10 -1
- flwr/server/driver/inmemory_driver.py +17 -20
- flwr/server/run_serverapp.py +2 -13
- flwr/server/server_app.py +93 -20
- flwr/server/superlink/driver/serverappio_servicer.py +25 -27
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +2 -2
- flwr/server/superlink/fleet/message_handler/message_handler.py +8 -12
- flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
- flwr/server/superlink/fleet/vce/vce_api.py +32 -35
- flwr/server/superlink/linkstate/in_memory_linkstate.py +140 -126
- flwr/server/superlink/linkstate/linkstate.py +47 -60
- flwr/server/superlink/linkstate/sqlite_linkstate.py +210 -276
- flwr/server/superlink/linkstate/utils.py +91 -119
- flwr/server/utils/__init__.py +2 -2
- flwr/server/utils/validator.py +53 -68
- flwr/server/workflow/default_workflows.py +4 -1
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +3 -3
- flwr/superexec/app.py +0 -14
- flwr/superexec/exec_servicer.py +4 -4
- flwr/superexec/exec_user_auth_interceptor.py +5 -3
- {flwr-1.15.2.dist-info → flwr-1.16.0.dist-info}/METADATA +4 -4
- {flwr-1.15.2.dist-info → flwr-1.16.0.dist-info}/RECORD +63 -66
- {flwr-1.15.2.dist-info → flwr-1.16.0.dist-info}/entry_points.txt +0 -3
- flwr/client/message_handler/task_handler.py +0 -37
- flwr/proto/task_pb2.py +0 -33
- flwr/proto/task_pb2.pyi +0 -100
- flwr/proto/task_pb2_grpc.py +0 -4
- flwr/proto/task_pb2_grpc.pyi +0 -4
- {flwr-1.15.2.dist-info → flwr-1.16.0.dist-info}/LICENSE +0 -0
- {flwr-1.15.2.dist-info → flwr-1.16.0.dist-info}/WHEEL +0 -0
|
@@ -23,7 +23,7 @@ from logging import ERROR, WARNING
|
|
|
23
23
|
from typing import Optional
|
|
24
24
|
from uuid import UUID, uuid4
|
|
25
25
|
|
|
26
|
-
from flwr.common import Context, log, now
|
|
26
|
+
from flwr.common import Context, Message, log, now
|
|
27
27
|
from flwr.common.constant import (
|
|
28
28
|
MESSAGE_TTL_TOLERANCE,
|
|
29
29
|
NODE_ID_NUM_BYTES,
|
|
@@ -33,16 +33,15 @@ from flwr.common.constant import (
|
|
|
33
33
|
)
|
|
34
34
|
from flwr.common.record import ConfigsRecord
|
|
35
35
|
from flwr.common.typing import Run, RunStatus, UserConfig
|
|
36
|
-
from flwr.proto.task_pb2 import TaskIns, TaskRes # pylint: disable=E0611
|
|
37
36
|
from flwr.server.superlink.linkstate.linkstate import LinkState
|
|
38
|
-
from flwr.server.utils import
|
|
37
|
+
from flwr.server.utils import validate_message
|
|
39
38
|
|
|
40
39
|
from .utils import (
|
|
41
40
|
generate_rand_int_from_bytes,
|
|
42
41
|
has_valid_sub_status,
|
|
43
42
|
is_valid_transition,
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
verify_found_message_replies,
|
|
44
|
+
verify_message_ids,
|
|
46
45
|
)
|
|
47
46
|
|
|
48
47
|
|
|
@@ -69,227 +68,242 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
|
|
|
69
68
|
self.run_ids: dict[int, RunRecord] = {}
|
|
70
69
|
self.contexts: dict[int, Context] = {}
|
|
71
70
|
self.federation_options: dict[int, ConfigsRecord] = {}
|
|
72
|
-
self.
|
|
73
|
-
self.
|
|
74
|
-
self.
|
|
71
|
+
self.message_ins_store: dict[UUID, Message] = {}
|
|
72
|
+
self.message_res_store: dict[UUID, Message] = {}
|
|
73
|
+
self.message_ins_id_to_message_res_id: dict[UUID, UUID] = {}
|
|
75
74
|
|
|
76
75
|
self.node_public_keys: set[bytes] = set()
|
|
77
76
|
|
|
78
77
|
self.lock = threading.RLock()
|
|
79
78
|
|
|
80
|
-
def
|
|
81
|
-
"""Store one
|
|
82
|
-
# Validate
|
|
83
|
-
errors =
|
|
79
|
+
def store_message_ins(self, message: Message) -> Optional[UUID]:
|
|
80
|
+
"""Store one Message."""
|
|
81
|
+
# Validate message
|
|
82
|
+
errors = validate_message(message, is_reply_message=False)
|
|
84
83
|
if any(errors):
|
|
85
84
|
log(ERROR, errors)
|
|
86
85
|
return None
|
|
87
86
|
# Validate run_id
|
|
88
|
-
if
|
|
89
|
-
log(ERROR, "Invalid run ID for
|
|
87
|
+
if message.metadata.run_id not in self.run_ids:
|
|
88
|
+
log(ERROR, "Invalid run ID for Message: %s", message.metadata.run_id)
|
|
90
89
|
return None
|
|
91
90
|
# Validate source node ID
|
|
92
|
-
if
|
|
91
|
+
if message.metadata.src_node_id != SUPERLINK_NODE_ID:
|
|
93
92
|
log(
|
|
94
93
|
ERROR,
|
|
95
|
-
"Invalid source node ID for
|
|
96
|
-
|
|
94
|
+
"Invalid source node ID for Message: %s",
|
|
95
|
+
message.metadata.src_node_id,
|
|
97
96
|
)
|
|
98
97
|
return None
|
|
99
98
|
# Validate destination node ID
|
|
100
|
-
if
|
|
99
|
+
if message.metadata.dst_node_id not in self.node_ids:
|
|
101
100
|
log(
|
|
102
101
|
ERROR,
|
|
103
|
-
"Invalid destination node ID for
|
|
104
|
-
|
|
102
|
+
"Invalid destination node ID for Message: %s",
|
|
103
|
+
message.metadata.dst_node_id,
|
|
105
104
|
)
|
|
106
105
|
return None
|
|
107
106
|
|
|
108
|
-
# Create
|
|
109
|
-
|
|
107
|
+
# Create message_id
|
|
108
|
+
message_id = uuid4()
|
|
110
109
|
|
|
111
|
-
# Store
|
|
112
|
-
|
|
110
|
+
# Store Message
|
|
111
|
+
# pylint: disable-next=W0212
|
|
112
|
+
message.metadata._message_id = str(message_id) # type: ignore
|
|
113
113
|
with self.lock:
|
|
114
|
-
self.
|
|
114
|
+
self.message_ins_store[message_id] = message
|
|
115
115
|
|
|
116
|
-
# Return the new
|
|
117
|
-
return
|
|
116
|
+
# Return the new message_id
|
|
117
|
+
return message_id
|
|
118
118
|
|
|
119
|
-
def
|
|
120
|
-
"""Get all
|
|
119
|
+
def get_message_ins(self, node_id: int, limit: Optional[int]) -> list[Message]:
|
|
120
|
+
"""Get all Messages that have not been delivered yet."""
|
|
121
121
|
if limit is not None and limit < 1:
|
|
122
122
|
raise AssertionError("`limit` must be >= 1")
|
|
123
123
|
|
|
124
|
-
# Find
|
|
125
|
-
|
|
124
|
+
# Find Message for node_id that were not delivered yet
|
|
125
|
+
message_ins_list: list[Message] = []
|
|
126
126
|
current_time = time.time()
|
|
127
127
|
with self.lock:
|
|
128
|
-
for _,
|
|
128
|
+
for _, msg_ins in self.message_ins_store.items():
|
|
129
129
|
if (
|
|
130
|
-
|
|
131
|
-
and
|
|
132
|
-
and
|
|
130
|
+
msg_ins.metadata.dst_node_id == node_id
|
|
131
|
+
and msg_ins.metadata.delivered_at == ""
|
|
132
|
+
and msg_ins.metadata.created_at + msg_ins.metadata.ttl
|
|
133
|
+
> current_time
|
|
133
134
|
):
|
|
134
|
-
|
|
135
|
-
if limit and len(
|
|
135
|
+
message_ins_list.append(msg_ins)
|
|
136
|
+
if limit and len(message_ins_list) == limit:
|
|
136
137
|
break
|
|
137
138
|
|
|
138
139
|
# Mark all of them as delivered
|
|
139
140
|
delivered_at = now().isoformat()
|
|
140
|
-
for
|
|
141
|
-
|
|
141
|
+
for msg_ins in message_ins_list:
|
|
142
|
+
msg_ins.metadata.delivered_at = delivered_at
|
|
142
143
|
|
|
143
|
-
# Return
|
|
144
|
-
return
|
|
144
|
+
# Return list of messages
|
|
145
|
+
return message_ins_list
|
|
145
146
|
|
|
146
147
|
# pylint: disable=R0911
|
|
147
|
-
def
|
|
148
|
-
"""Store one
|
|
149
|
-
# Validate
|
|
150
|
-
errors =
|
|
148
|
+
def store_message_res(self, message: Message) -> Optional[UUID]:
|
|
149
|
+
"""Store one Message."""
|
|
150
|
+
# Validate message
|
|
151
|
+
errors = validate_message(message, is_reply_message=True)
|
|
151
152
|
if any(errors):
|
|
152
153
|
log(ERROR, errors)
|
|
153
154
|
return None
|
|
154
155
|
|
|
156
|
+
res_metadata = message.metadata
|
|
155
157
|
with self.lock:
|
|
156
|
-
# Check if the
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
# Check if the Message it is replying to exists and is valid
|
|
159
|
+
msg_ins_id = res_metadata.reply_to_message
|
|
160
|
+
msg_ins = self.message_ins_store.get(UUID(msg_ins_id))
|
|
159
161
|
|
|
160
|
-
# Ensure that
|
|
162
|
+
# Ensure that dst_node_id of original Message matches the src_node_id of
|
|
163
|
+
# reply Message.
|
|
161
164
|
if (
|
|
162
|
-
|
|
163
|
-
and
|
|
164
|
-
and
|
|
165
|
+
msg_ins
|
|
166
|
+
and message
|
|
167
|
+
and msg_ins.metadata.dst_node_id != res_metadata.src_node_id
|
|
165
168
|
):
|
|
166
169
|
return None
|
|
167
170
|
|
|
168
|
-
if
|
|
169
|
-
log(
|
|
171
|
+
if msg_ins is None:
|
|
172
|
+
log(
|
|
173
|
+
ERROR,
|
|
174
|
+
"Message with ID %s does not exist.",
|
|
175
|
+
msg_ins_id,
|
|
176
|
+
)
|
|
170
177
|
return None
|
|
171
178
|
|
|
172
|
-
|
|
179
|
+
ins_metadata = msg_ins.metadata
|
|
180
|
+
if ins_metadata.created_at + ins_metadata.ttl <= time.time():
|
|
173
181
|
log(
|
|
174
182
|
ERROR,
|
|
175
|
-
"Failed to store
|
|
176
|
-
|
|
183
|
+
"Failed to store Message: the message it is replying to "
|
|
184
|
+
"(with ID %s) has expired",
|
|
185
|
+
msg_ins_id,
|
|
177
186
|
)
|
|
178
187
|
return None
|
|
179
188
|
|
|
180
|
-
# Fail if the
|
|
181
|
-
# expiration time of the
|
|
182
|
-
# Condition:
|
|
183
|
-
#
|
|
189
|
+
# Fail if the Message TTL exceeds the
|
|
190
|
+
# expiration time of the Message it replies to.
|
|
191
|
+
# Condition: ins_metadata.created_at + ins_metadata.ttl ≥
|
|
192
|
+
# res_metadata.created_at + res_metadata.ttl
|
|
184
193
|
# A small tolerance is introduced to account
|
|
185
194
|
# for floating-point precision issues.
|
|
186
195
|
max_allowed_ttl = (
|
|
187
|
-
|
|
196
|
+
ins_metadata.created_at + ins_metadata.ttl - res_metadata.created_at
|
|
188
197
|
)
|
|
189
|
-
if
|
|
190
|
-
|
|
198
|
+
if res_metadata.ttl and (
|
|
199
|
+
res_metadata.ttl - max_allowed_ttl > MESSAGE_TTL_TOLERANCE
|
|
191
200
|
):
|
|
192
201
|
log(
|
|
193
202
|
WARNING,
|
|
194
|
-
"Received
|
|
195
|
-
"
|
|
196
|
-
|
|
203
|
+
"Received Message with TTL %.2f exceeding the allowed maximum "
|
|
204
|
+
"TTL %.2f.",
|
|
205
|
+
res_metadata.ttl,
|
|
197
206
|
max_allowed_ttl,
|
|
198
207
|
)
|
|
199
208
|
return None
|
|
200
209
|
|
|
201
210
|
# Validate run_id
|
|
202
|
-
if
|
|
203
|
-
log(ERROR, "`run_id` is invalid")
|
|
211
|
+
if res_metadata.run_id != ins_metadata.run_id:
|
|
212
|
+
log(ERROR, "`metadata.run_id` is invalid")
|
|
204
213
|
return None
|
|
205
214
|
|
|
206
|
-
# Create
|
|
207
|
-
|
|
215
|
+
# Create message_id
|
|
216
|
+
message_id = uuid4()
|
|
208
217
|
|
|
209
|
-
# Store
|
|
210
|
-
|
|
218
|
+
# Store Message
|
|
219
|
+
# pylint: disable-next=W0212
|
|
220
|
+
message.metadata._message_id = str(message_id) # type: ignore
|
|
211
221
|
with self.lock:
|
|
212
|
-
self.
|
|
213
|
-
self.
|
|
222
|
+
self.message_res_store[message_id] = message
|
|
223
|
+
self.message_ins_id_to_message_res_id[UUID(msg_ins_id)] = message_id
|
|
214
224
|
|
|
215
|
-
# Return the new
|
|
216
|
-
return
|
|
225
|
+
# Return the new message_id
|
|
226
|
+
return message_id
|
|
217
227
|
|
|
218
|
-
def
|
|
219
|
-
"""Get
|
|
220
|
-
ret: dict[UUID,
|
|
228
|
+
def get_message_res(self, message_ids: set[UUID]) -> list[Message]:
|
|
229
|
+
"""Get reply Messages for the given Message IDs."""
|
|
230
|
+
ret: dict[UUID, Message] = {}
|
|
221
231
|
|
|
222
232
|
with self.lock:
|
|
223
233
|
current = time.time()
|
|
224
234
|
|
|
225
|
-
# Verify
|
|
226
|
-
ret =
|
|
227
|
-
|
|
228
|
-
|
|
235
|
+
# Verify Messge IDs
|
|
236
|
+
ret = verify_message_ids(
|
|
237
|
+
inquired_message_ids=message_ids,
|
|
238
|
+
found_message_ins_dict=self.message_ins_store,
|
|
229
239
|
current_time=current,
|
|
230
240
|
)
|
|
231
241
|
|
|
232
|
-
# Find all
|
|
233
|
-
|
|
234
|
-
for
|
|
235
|
-
# If
|
|
236
|
-
if
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
242
|
+
# Find all reply Messages
|
|
243
|
+
message_res_found: list[Message] = []
|
|
244
|
+
for message_id in message_ids:
|
|
245
|
+
# If Message exists and is not delivered, add it to the list
|
|
246
|
+
if message_res_id := self.message_ins_id_to_message_res_id.get(
|
|
247
|
+
message_id
|
|
248
|
+
):
|
|
249
|
+
message_res = self.message_res_store[message_res_id]
|
|
250
|
+
if message_res.metadata.delivered_at == "":
|
|
251
|
+
message_res_found.append(message_res)
|
|
252
|
+
tmp_ret_dict = verify_found_message_replies(
|
|
253
|
+
inquired_message_ids=message_ids,
|
|
254
|
+
found_message_ins_dict=self.message_ins_store,
|
|
255
|
+
found_message_res_list=message_res_found,
|
|
244
256
|
current_time=current,
|
|
245
257
|
)
|
|
246
258
|
ret.update(tmp_ret_dict)
|
|
247
259
|
|
|
248
|
-
# Mark existing
|
|
260
|
+
# Mark existing reply Messages to be returned as delivered
|
|
249
261
|
delivered_at = now().isoformat()
|
|
250
|
-
for
|
|
251
|
-
|
|
262
|
+
for message_res in message_res_found:
|
|
263
|
+
message_res.metadata.delivered_at = delivered_at
|
|
252
264
|
|
|
253
265
|
return list(ret.values())
|
|
254
266
|
|
|
255
|
-
def
|
|
256
|
-
"""Delete
|
|
257
|
-
if not
|
|
267
|
+
def delete_messages(self, message_ins_ids: set[UUID]) -> None:
|
|
268
|
+
"""Delete a Message and its reply based on provided Message IDs."""
|
|
269
|
+
if not message_ins_ids:
|
|
258
270
|
return
|
|
259
271
|
|
|
260
272
|
with self.lock:
|
|
261
|
-
for
|
|
262
|
-
# Delete
|
|
263
|
-
if
|
|
264
|
-
del self.
|
|
265
|
-
# Delete
|
|
266
|
-
if
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
+
for message_id in message_ins_ids:
|
|
274
|
+
# Delete Messages
|
|
275
|
+
if message_id in self.message_ins_store:
|
|
276
|
+
del self.message_ins_store[message_id]
|
|
277
|
+
# Delete Message replies
|
|
278
|
+
if message_id in self.message_ins_id_to_message_res_id:
|
|
279
|
+
message_res_id = self.message_ins_id_to_message_res_id.pop(
|
|
280
|
+
message_id
|
|
281
|
+
)
|
|
282
|
+
del self.message_res_store[message_res_id]
|
|
283
|
+
|
|
284
|
+
def get_message_ids_from_run_id(self, run_id: int) -> set[UUID]:
|
|
285
|
+
"""Get all instruction Message IDs for the given run_id."""
|
|
286
|
+
message_id_list: set[UUID] = set()
|
|
273
287
|
with self.lock:
|
|
274
|
-
for
|
|
275
|
-
if
|
|
276
|
-
|
|
288
|
+
for message_id, message in self.message_ins_store.items():
|
|
289
|
+
if message.metadata.run_id == run_id:
|
|
290
|
+
message_id_list.add(message_id)
|
|
277
291
|
|
|
278
|
-
return
|
|
292
|
+
return message_id_list
|
|
279
293
|
|
|
280
|
-
def
|
|
281
|
-
"""Calculate the number of
|
|
294
|
+
def num_message_ins(self) -> int:
|
|
295
|
+
"""Calculate the number of instruction Messages in store.
|
|
282
296
|
|
|
283
|
-
This includes delivered but not yet deleted
|
|
297
|
+
This includes delivered but not yet deleted.
|
|
284
298
|
"""
|
|
285
|
-
return len(self.
|
|
299
|
+
return len(self.message_ins_store)
|
|
286
300
|
|
|
287
|
-
def
|
|
288
|
-
"""Calculate the number of
|
|
301
|
+
def num_message_res(self) -> int:
|
|
302
|
+
"""Calculate the number of reply Messages in store.
|
|
289
303
|
|
|
290
|
-
This includes delivered but not yet deleted
|
|
304
|
+
This includes delivered but not yet deleted.
|
|
291
305
|
"""
|
|
292
|
-
return len(self.
|
|
306
|
+
return len(self.message_res_store)
|
|
293
307
|
|
|
294
308
|
def create_node(self, ping_interval: float) -> int:
|
|
295
309
|
"""Create, store in the link state, and return `node_id`."""
|
|
@@ -19,126 +19,113 @@ import abc
|
|
|
19
19
|
from typing import Optional
|
|
20
20
|
from uuid import UUID
|
|
21
21
|
|
|
22
|
-
from flwr.common import Context
|
|
22
|
+
from flwr.common import Context, Message
|
|
23
23
|
from flwr.common.record import ConfigsRecord
|
|
24
24
|
from flwr.common.typing import Run, RunStatus, UserConfig
|
|
25
|
-
from flwr.proto.task_pb2 import TaskIns, TaskRes # pylint: disable=E0611
|
|
26
25
|
|
|
27
26
|
|
|
28
27
|
class LinkState(abc.ABC): # pylint: disable=R0904
|
|
29
28
|
"""Abstract LinkState."""
|
|
30
29
|
|
|
31
30
|
@abc.abstractmethod
|
|
32
|
-
def
|
|
33
|
-
"""Store one
|
|
31
|
+
def store_message_ins(self, message: Message) -> Optional[UUID]:
|
|
32
|
+
"""Store one Message.
|
|
34
33
|
|
|
35
34
|
Usually, the ServerAppIo API calls this to schedule instructions.
|
|
36
35
|
|
|
37
|
-
Stores the value of the `
|
|
38
|
-
returns the `
|
|
39
|
-
storing the `
|
|
36
|
+
Stores the value of the `message` in the link state and, if successful,
|
|
37
|
+
returns the `message_id` (UUID) of the `message`. If, for any reason,
|
|
38
|
+
storing the `message` fails, `None` is returned.
|
|
40
39
|
|
|
41
40
|
Constraints
|
|
42
41
|
-----------
|
|
43
|
-
`
|
|
42
|
+
`message.metadata.dst_node_id` MUST be set (not constant.SUPERLINK_NODE_ID)
|
|
44
43
|
|
|
45
|
-
If `
|
|
46
|
-
storing the `
|
|
44
|
+
If `message.metadata.run_id` is invalid, then
|
|
45
|
+
storing the `message` MUST fail.
|
|
47
46
|
"""
|
|
48
47
|
|
|
49
48
|
@abc.abstractmethod
|
|
50
|
-
def
|
|
51
|
-
"""Get
|
|
49
|
+
def get_message_ins(self, node_id: int, limit: Optional[int]) -> list[Message]:
|
|
50
|
+
"""Get zero or more `Message` objects for the provided `node_id`.
|
|
52
51
|
|
|
53
52
|
Usually, the Fleet API calls this for Nodes planning to work on one or more
|
|
54
|
-
|
|
53
|
+
Message.
|
|
55
54
|
|
|
56
55
|
Constraints
|
|
57
56
|
-----------
|
|
58
|
-
Retrieve all
|
|
57
|
+
Retrieve all Message where the `message.metadata.dst_node_id` equals `node_id`.
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
2. the `task_ins.task.delivered_at` equals `""`.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
If `delivered_at` MUST BE set (not `""`) otherwise the TaskIns MUST not be in
|
|
65
|
-
the result.
|
|
66
|
-
|
|
67
|
-
If `limit` is not `None`, return, at most, `limit` number of `task_ins`. If
|
|
59
|
+
If `limit` is not `None`, return, at most, `limit` number of `message`. If
|
|
68
60
|
`limit` is set, it has to be greater zero.
|
|
69
61
|
"""
|
|
70
62
|
|
|
71
63
|
@abc.abstractmethod
|
|
72
|
-
def
|
|
73
|
-
"""Store one
|
|
64
|
+
def store_message_res(self, message: Message) -> Optional[UUID]:
|
|
65
|
+
"""Store one Message.
|
|
74
66
|
|
|
75
67
|
Usually, the Fleet API calls this for Nodes returning results.
|
|
76
68
|
|
|
77
|
-
Stores the
|
|
78
|
-
the `
|
|
69
|
+
Stores the Message and, if successful, returns the `message_id` (UUID) of
|
|
70
|
+
the `message`. If storing the `message` fails, `None` is returned.
|
|
79
71
|
|
|
80
72
|
Constraints
|
|
81
73
|
-----------
|
|
74
|
+
`message.metadata.dst_node_id` MUST be set (not constant.SUPERLINK_NODE_ID)
|
|
82
75
|
|
|
83
|
-
`
|
|
84
|
-
|
|
85
|
-
If `task_res.run_id` is invalid, then
|
|
86
|
-
storing the `task_res` MUST fail.
|
|
76
|
+
If `message.metadata.run_id` is invalid, then
|
|
77
|
+
storing the `message` MUST fail.
|
|
87
78
|
"""
|
|
88
79
|
|
|
89
80
|
@abc.abstractmethod
|
|
90
|
-
def
|
|
91
|
-
"""Get
|
|
81
|
+
def get_message_res(self, message_ids: set[UUID]) -> list[Message]:
|
|
82
|
+
"""Get reply Messages for the given Message IDs.
|
|
92
83
|
|
|
93
84
|
This method is typically called by the ServerAppIo API to obtain
|
|
94
|
-
results (
|
|
95
|
-
For each
|
|
85
|
+
results (type Message) for previously scheduled instructions (type Message).
|
|
86
|
+
For each message_id passed, this method returns one of the following responses:
|
|
96
87
|
|
|
97
|
-
- An error
|
|
98
|
-
|
|
99
|
-
-
|
|
100
|
-
-
|
|
88
|
+
- An error Message if there was no message registered with such message IDs
|
|
89
|
+
or has expired.
|
|
90
|
+
- An error Message if the reply Message exists but has expired.
|
|
91
|
+
- The reply Message.
|
|
92
|
+
- Nothing if the Message with the passed message_id is still valid and waiting
|
|
93
|
+
for a reply Message.
|
|
101
94
|
|
|
102
95
|
Parameters
|
|
103
96
|
----------
|
|
104
|
-
|
|
105
|
-
A set of
|
|
97
|
+
message_ids : set[UUID]
|
|
98
|
+
A set of Message IDs used to retrieve reply Messages responding to them.
|
|
106
99
|
|
|
107
100
|
Returns
|
|
108
101
|
-------
|
|
109
|
-
list[
|
|
110
|
-
A list of
|
|
111
|
-
|
|
102
|
+
list[Message]
|
|
103
|
+
A list of reply Message responding to the given message IDs or Messages
|
|
104
|
+
carrying an Error.
|
|
112
105
|
"""
|
|
113
106
|
|
|
114
107
|
@abc.abstractmethod
|
|
115
|
-
def
|
|
116
|
-
"""Calculate the number of
|
|
117
|
-
|
|
118
|
-
This includes delivered but not yet deleted task_ins.
|
|
119
|
-
"""
|
|
108
|
+
def num_message_ins(self) -> int:
|
|
109
|
+
"""Calculate the number of Messages awaiting a reply."""
|
|
120
110
|
|
|
121
111
|
@abc.abstractmethod
|
|
122
|
-
def
|
|
123
|
-
"""Calculate the number of
|
|
124
|
-
|
|
125
|
-
This includes delivered but not yet deleted task_res.
|
|
126
|
-
"""
|
|
112
|
+
def num_message_res(self) -> int:
|
|
113
|
+
"""Calculate the number of reply Messages in store."""
|
|
127
114
|
|
|
128
115
|
@abc.abstractmethod
|
|
129
|
-
def
|
|
130
|
-
"""Delete
|
|
116
|
+
def delete_messages(self, message_ins_ids: set[UUID]) -> None:
|
|
117
|
+
"""Delete a Message and its reply based on provided Message IDs.
|
|
131
118
|
|
|
132
119
|
Parameters
|
|
133
120
|
----------
|
|
134
|
-
|
|
135
|
-
A set of
|
|
136
|
-
|
|
121
|
+
message_ins_ids : set[UUID]
|
|
122
|
+
A set of Message IDs. For each ID in the set, the corresponding
|
|
123
|
+
Message and its associated reply Message will be deleted.
|
|
137
124
|
"""
|
|
138
125
|
|
|
139
126
|
@abc.abstractmethod
|
|
140
|
-
def
|
|
141
|
-
"""Get all
|
|
127
|
+
def get_message_ids_from_run_id(self, run_id: int) -> set[UUID]:
|
|
128
|
+
"""Get all instruction Message IDs for the given run_id."""
|
|
142
129
|
|
|
143
130
|
@abc.abstractmethod
|
|
144
131
|
def create_node(self, ping_interval: float) -> int:
|