opengris-scaler 1.12.37__cp38-cp38-musllinux_1_2_x86_64.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.
Files changed (196) hide show
  1. opengris_scaler-1.12.37.dist-info/METADATA +730 -0
  2. opengris_scaler-1.12.37.dist-info/RECORD +196 -0
  3. opengris_scaler-1.12.37.dist-info/WHEEL +5 -0
  4. opengris_scaler-1.12.37.dist-info/entry_points.txt +10 -0
  5. opengris_scaler-1.12.37.dist-info/licenses/LICENSE +201 -0
  6. opengris_scaler-1.12.37.dist-info/licenses/LICENSE.spdx +7 -0
  7. opengris_scaler-1.12.37.dist-info/licenses/NOTICE +8 -0
  8. opengris_scaler.libs/libcapnp-1-e88d5415.0.1.so +0 -0
  9. opengris_scaler.libs/libgcc_s-2298274a.so.1 +0 -0
  10. opengris_scaler.libs/libkj-1-9bebd8ac.0.1.so +0 -0
  11. opengris_scaler.libs/libstdc++-08d5c7eb.so.6.0.33 +0 -0
  12. scaler/__init__.py +14 -0
  13. scaler/about.py +5 -0
  14. scaler/client/__init__.py +0 -0
  15. scaler/client/agent/__init__.py +0 -0
  16. scaler/client/agent/client_agent.py +218 -0
  17. scaler/client/agent/disconnect_manager.py +27 -0
  18. scaler/client/agent/future_manager.py +112 -0
  19. scaler/client/agent/heartbeat_manager.py +74 -0
  20. scaler/client/agent/mixins.py +89 -0
  21. scaler/client/agent/object_manager.py +98 -0
  22. scaler/client/agent/task_manager.py +64 -0
  23. scaler/client/client.py +672 -0
  24. scaler/client/future.py +252 -0
  25. scaler/client/object_buffer.py +129 -0
  26. scaler/client/object_reference.py +25 -0
  27. scaler/client/serializer/__init__.py +0 -0
  28. scaler/client/serializer/default.py +16 -0
  29. scaler/client/serializer/mixins.py +38 -0
  30. scaler/cluster/__init__.py +0 -0
  31. scaler/cluster/cluster.py +95 -0
  32. scaler/cluster/combo.py +157 -0
  33. scaler/cluster/object_storage_server.py +45 -0
  34. scaler/cluster/scheduler.py +86 -0
  35. scaler/config/__init__.py +0 -0
  36. scaler/config/common/__init__.py +0 -0
  37. scaler/config/common/logging.py +41 -0
  38. scaler/config/common/web.py +18 -0
  39. scaler/config/common/worker.py +65 -0
  40. scaler/config/common/worker_adapter.py +28 -0
  41. scaler/config/config_class.py +317 -0
  42. scaler/config/defaults.py +94 -0
  43. scaler/config/mixins.py +20 -0
  44. scaler/config/section/__init__.py +0 -0
  45. scaler/config/section/cluster.py +66 -0
  46. scaler/config/section/ecs_worker_adapter.py +78 -0
  47. scaler/config/section/native_worker_adapter.py +30 -0
  48. scaler/config/section/object_storage_server.py +13 -0
  49. scaler/config/section/scheduler.py +126 -0
  50. scaler/config/section/symphony_worker_adapter.py +35 -0
  51. scaler/config/section/top.py +16 -0
  52. scaler/config/section/webui.py +16 -0
  53. scaler/config/types/__init__.py +0 -0
  54. scaler/config/types/network_backend.py +12 -0
  55. scaler/config/types/object_storage_server.py +45 -0
  56. scaler/config/types/worker.py +67 -0
  57. scaler/config/types/zmq.py +83 -0
  58. scaler/entry_points/__init__.py +0 -0
  59. scaler/entry_points/cluster.py +10 -0
  60. scaler/entry_points/object_storage_server.py +26 -0
  61. scaler/entry_points/scheduler.py +51 -0
  62. scaler/entry_points/top.py +272 -0
  63. scaler/entry_points/webui.py +6 -0
  64. scaler/entry_points/worker_adapter_ecs.py +22 -0
  65. scaler/entry_points/worker_adapter_native.py +31 -0
  66. scaler/entry_points/worker_adapter_symphony.py +26 -0
  67. scaler/io/__init__.py +0 -0
  68. scaler/io/async_binder.py +89 -0
  69. scaler/io/async_connector.py +95 -0
  70. scaler/io/async_object_storage_connector.py +225 -0
  71. scaler/io/mixins.py +154 -0
  72. scaler/io/sync_connector.py +68 -0
  73. scaler/io/sync_object_storage_connector.py +249 -0
  74. scaler/io/sync_subscriber.py +83 -0
  75. scaler/io/utility.py +80 -0
  76. scaler/io/ymq/__init__.py +0 -0
  77. scaler/io/ymq/_ymq.pyi +95 -0
  78. scaler/io/ymq/_ymq.so +0 -0
  79. scaler/io/ymq/ymq.py +138 -0
  80. scaler/io/ymq_async_object_storage_connector.py +184 -0
  81. scaler/io/ymq_sync_object_storage_connector.py +184 -0
  82. scaler/object_storage/__init__.py +0 -0
  83. scaler/object_storage/object_storage_server.so +0 -0
  84. scaler/protocol/__init__.py +0 -0
  85. scaler/protocol/capnp/__init__.py +0 -0
  86. scaler/protocol/capnp/_python.py +6 -0
  87. scaler/protocol/capnp/common.capnp +68 -0
  88. scaler/protocol/capnp/message.capnp +218 -0
  89. scaler/protocol/capnp/object_storage.capnp +57 -0
  90. scaler/protocol/capnp/status.capnp +73 -0
  91. scaler/protocol/introduction.md +105 -0
  92. scaler/protocol/python/__init__.py +0 -0
  93. scaler/protocol/python/common.py +140 -0
  94. scaler/protocol/python/message.py +751 -0
  95. scaler/protocol/python/mixins.py +13 -0
  96. scaler/protocol/python/object_storage.py +118 -0
  97. scaler/protocol/python/status.py +279 -0
  98. scaler/protocol/worker.md +228 -0
  99. scaler/scheduler/__init__.py +0 -0
  100. scaler/scheduler/allocate_policy/__init__.py +0 -0
  101. scaler/scheduler/allocate_policy/allocate_policy.py +9 -0
  102. scaler/scheduler/allocate_policy/capability_allocate_policy.py +280 -0
  103. scaler/scheduler/allocate_policy/even_load_allocate_policy.py +159 -0
  104. scaler/scheduler/allocate_policy/mixins.py +55 -0
  105. scaler/scheduler/controllers/__init__.py +0 -0
  106. scaler/scheduler/controllers/balance_controller.py +65 -0
  107. scaler/scheduler/controllers/client_controller.py +131 -0
  108. scaler/scheduler/controllers/config_controller.py +31 -0
  109. scaler/scheduler/controllers/graph_controller.py +424 -0
  110. scaler/scheduler/controllers/information_controller.py +81 -0
  111. scaler/scheduler/controllers/mixins.py +194 -0
  112. scaler/scheduler/controllers/object_controller.py +147 -0
  113. scaler/scheduler/controllers/scaling_policies/__init__.py +0 -0
  114. scaler/scheduler/controllers/scaling_policies/fixed_elastic.py +145 -0
  115. scaler/scheduler/controllers/scaling_policies/mixins.py +10 -0
  116. scaler/scheduler/controllers/scaling_policies/null.py +14 -0
  117. scaler/scheduler/controllers/scaling_policies/types.py +9 -0
  118. scaler/scheduler/controllers/scaling_policies/utility.py +20 -0
  119. scaler/scheduler/controllers/scaling_policies/vanilla.py +95 -0
  120. scaler/scheduler/controllers/task_controller.py +376 -0
  121. scaler/scheduler/controllers/worker_controller.py +169 -0
  122. scaler/scheduler/object_usage/__init__.py +0 -0
  123. scaler/scheduler/object_usage/object_tracker.py +131 -0
  124. scaler/scheduler/scheduler.py +251 -0
  125. scaler/scheduler/task/__init__.py +0 -0
  126. scaler/scheduler/task/task_state_machine.py +92 -0
  127. scaler/scheduler/task/task_state_manager.py +61 -0
  128. scaler/ui/__init__.py +0 -0
  129. scaler/ui/common/__init__.py +0 -0
  130. scaler/ui/common/constants.py +9 -0
  131. scaler/ui/common/live_display.py +147 -0
  132. scaler/ui/common/memory_window.py +146 -0
  133. scaler/ui/common/setting_page.py +40 -0
  134. scaler/ui/common/task_graph.py +840 -0
  135. scaler/ui/common/task_log.py +111 -0
  136. scaler/ui/common/utility.py +66 -0
  137. scaler/ui/common/webui.py +80 -0
  138. scaler/ui/common/worker_processors.py +104 -0
  139. scaler/ui/v1.py +76 -0
  140. scaler/ui/v2.py +102 -0
  141. scaler/ui/webui.py +21 -0
  142. scaler/utility/__init__.py +0 -0
  143. scaler/utility/debug.py +19 -0
  144. scaler/utility/event_list.py +63 -0
  145. scaler/utility/event_loop.py +58 -0
  146. scaler/utility/exceptions.py +42 -0
  147. scaler/utility/formatter.py +44 -0
  148. scaler/utility/graph/__init__.py +0 -0
  149. scaler/utility/graph/optimization.py +27 -0
  150. scaler/utility/graph/topological_sorter.py +11 -0
  151. scaler/utility/graph/topological_sorter_graphblas.py +174 -0
  152. scaler/utility/identifiers.py +107 -0
  153. scaler/utility/logging/__init__.py +0 -0
  154. scaler/utility/logging/decorators.py +25 -0
  155. scaler/utility/logging/scoped_logger.py +33 -0
  156. scaler/utility/logging/utility.py +183 -0
  157. scaler/utility/many_to_many_dict.py +123 -0
  158. scaler/utility/metadata/__init__.py +0 -0
  159. scaler/utility/metadata/profile_result.py +31 -0
  160. scaler/utility/metadata/task_flags.py +30 -0
  161. scaler/utility/mixins.py +13 -0
  162. scaler/utility/network_util.py +7 -0
  163. scaler/utility/one_to_many_dict.py +72 -0
  164. scaler/utility/queues/__init__.py +0 -0
  165. scaler/utility/queues/async_indexed_queue.py +37 -0
  166. scaler/utility/queues/async_priority_queue.py +70 -0
  167. scaler/utility/queues/async_sorted_priority_queue.py +45 -0
  168. scaler/utility/queues/indexed_queue.py +114 -0
  169. scaler/utility/serialization.py +9 -0
  170. scaler/version.txt +1 -0
  171. scaler/worker/__init__.py +0 -0
  172. scaler/worker/agent/__init__.py +0 -0
  173. scaler/worker/agent/heartbeat_manager.py +110 -0
  174. scaler/worker/agent/mixins.py +137 -0
  175. scaler/worker/agent/processor/__init__.py +0 -0
  176. scaler/worker/agent/processor/object_cache.py +107 -0
  177. scaler/worker/agent/processor/processor.py +285 -0
  178. scaler/worker/agent/processor/streaming_buffer.py +28 -0
  179. scaler/worker/agent/processor_holder.py +147 -0
  180. scaler/worker/agent/processor_manager.py +369 -0
  181. scaler/worker/agent/profiling_manager.py +109 -0
  182. scaler/worker/agent/task_manager.py +150 -0
  183. scaler/worker/agent/timeout_manager.py +19 -0
  184. scaler/worker/preload.py +84 -0
  185. scaler/worker/worker.py +265 -0
  186. scaler/worker_adapter/__init__.py +0 -0
  187. scaler/worker_adapter/common.py +26 -0
  188. scaler/worker_adapter/ecs.py +241 -0
  189. scaler/worker_adapter/native.py +138 -0
  190. scaler/worker_adapter/symphony/__init__.py +0 -0
  191. scaler/worker_adapter/symphony/callback.py +45 -0
  192. scaler/worker_adapter/symphony/heartbeat_manager.py +82 -0
  193. scaler/worker_adapter/symphony/message.py +24 -0
  194. scaler/worker_adapter/symphony/task_manager.py +289 -0
  195. scaler/worker_adapter/symphony/worker.py +204 -0
  196. scaler/worker_adapter/symphony/worker_adapter.py +123 -0
@@ -0,0 +1,317 @@
1
+ import dataclasses
2
+ import typing
3
+ from typing import Any, Dict, Type, TypeVar
4
+
5
+ from configargparse import ArgParser, ArgumentDefaultsHelpFormatter, TomlConfigParser
6
+
7
+ from scaler.config.mixins import ConfigType
8
+
9
+ T = TypeVar("T", bound="ConfigClass")
10
+
11
+
12
+ class ConfigClass:
13
+ """
14
+ An abstract interface for dataclasses where the fields define command line options,
15
+ config file options, and environment variables.
16
+
17
+ Subclasses of `ConfigClass` must be dataclasses.
18
+ Subclasses of `ConfigClass` are called "config classes".
19
+
20
+ ## Config Files
21
+
22
+ All options with a long name can be parsed from a TOML config file specified with `--config` or `-c`.
23
+ The section name is determined by the subclass' implementation of `.section_name()`.
24
+
25
+ ## Environment Variables
26
+
27
+ Any parameter can be configured to read from an environment variable by adding `env="NAME"` to the field metadata.
28
+
29
+ ```python
30
+ # can be set as --my-field on the command line or in a config file, or using the environment variable `NAME`
31
+ my_field: int = dataclasses.field(metadata=dict(env="NAME"))
32
+ ```
33
+
34
+ ## Precedence
35
+
36
+ When a parameter is supplied in multiple ways, the following precedence is observed:
37
+ command line > environment variables > config file > defaults
38
+
39
+ ## Customization
40
+
41
+ Any key values included in the field's metadata will be passed through to `.add_argument()`
42
+ and will always take precedence over derived values, except for `default`.
43
+
44
+ The value for `default` is taken from the field's default. Providing `default` in the field
45
+ metadata will result in a `TypeError`.
46
+
47
+ ```python
48
+ # passes through options
49
+ custom_field: int = dataclasses.field(
50
+ metadata=dict(
51
+ choices=["apples", "oranges"],
52
+ help="choose a fruit",
53
+ )
54
+ )
55
+
56
+ # TypeError!
57
+ bad: int = dataclasses.field(metadata=dict(default=0))
58
+ ```
59
+
60
+ ## Naming Fields
61
+
62
+ The name of the dataclass fields (replacing underscores with hyphens) is used
63
+ as the long option name for command line and config file parameters.
64
+ A short name for the argument can be provided in the field metadata using the `short` key.
65
+ The value is the short option name and must include the hyphen, e.g. `-n`.
66
+
67
+ There is one restriction: `--config` and `-c` are reserved for the config file.
68
+
69
+ ```python
70
+ # this will have long name --field-one, and no short name
71
+ field_one: int = 5
72
+
73
+ # sets a short name
74
+ field_two: int = dataclasses.field(default=5, metadata=dict(short="-f2"))
75
+ ```
76
+
77
+ ## Default Values
78
+
79
+ The default value of the field is also used as the default for the argument parser.
80
+
81
+ ```python
82
+ lang: str = "en"
83
+ name: str = dataclasses.field(default="Arthur")
84
+ ```
85
+
86
+ ## Positional Parameters
87
+
88
+ You can set `positional=True` in the metadata dict to make an argument positional.
89
+ In this case long and short names are ignored, use `name` to override the name of the option.
90
+ The position is dependent on field ordering.
91
+
92
+ ```python
93
+ # both of these are positional, and field one must be specified before field two
94
+ field_one: int = dataclasses.field(metadata=dict(positional=True))
95
+ field_two: int = dataclasses.field(metadata=dict(positional=True))
96
+ ```
97
+
98
+ ## Composition
99
+
100
+ Config classes can be composed. If a config class has fields that are config classes,
101
+ then the options of the child config class are inherited as if that child config class'
102
+ fields were added to the parent, for the purpose of parsing arguments. When the dataclass
103
+ is created, the structure is kept.
104
+
105
+ Care needs to be taken so that field names do not conflict with each other.
106
+ The name of fields in nested config classes are in the same namespace as those
107
+ in the parent and other nested config classes.
108
+
109
+ ## Parameter Types
110
+
111
+ The type of a field is used as the type in argument parsing, meaning that it must be able
112
+ to be directly constructed from a string.
113
+
114
+ Special handling is implemented for several common types:
115
+ - `bool`: parses from "true" and "false", and all upper/lower case variants
116
+ - subclasses of `ConfigType`: uses the `.from_string()` method
117
+ - `Optional[T]`, `T | None`: parsed as `T` and sets `required=False`
118
+ - `List[T]`, `list[T]`: parsed as `T`, and sets `nargs="*"`
119
+
120
+ For generic types, the `T` is parsed recursively following the rules here.
121
+ For example, `Optional[T]` where `T` is a subclass of `ConfigType`
122
+ will still use `T.from_string()`.
123
+
124
+ as usual, all of these can be overriden by setting the option in the metadata.
125
+
126
+ ```python
127
+ # this provides a custom `type` to parse the input as hexadecimal
128
+ # `type` must be a callable that accepts a string
129
+ # refer to the argparse docs for more
130
+ hex: int = dataclasses.field(metadata=dict(type=lambda s: int(s, 16)))
131
+
132
+ class MyConfigType(ConfigType):
133
+ ...
134
+
135
+ # this will work as expected
136
+ my_field: MyConfigType
137
+
138
+ # this requires special handling
139
+ tuples: Tuple[int, ...] = dataclasses.field(metadata=dict(type=int, nargs="*"))
140
+
141
+ # works automatically, defaults to `nargs="*"`
142
+ integers: List[int]
143
+
144
+ # ... but we can override that
145
+ integers2: List[int] = dataclasses.field(metadata=dict(nargs="+"))
146
+
147
+ # this will automatically have `required=False` set
148
+ maybe: Optional[str]
149
+ """
150
+
151
+ @classmethod
152
+ def configure_parser(cls: type, parser: ArgParser):
153
+ fields = dataclasses.fields(cls)
154
+
155
+ for field in fields:
156
+ if is_config_class(field.type):
157
+ field.type.configure_parser(parser) # type: ignore[union-attr]
158
+ continue
159
+
160
+ kwargs = dict(field.metadata)
161
+
162
+ # usually command line options use hyphens instead of underscores
163
+
164
+ if kwargs.pop("positional", False):
165
+ args = [kwargs.pop("name", field.name)]
166
+ else:
167
+ long_name = kwargs.pop("long", f"--{field.name.replace('_', '-')}")
168
+ if "short" in kwargs:
169
+ args = [long_name, kwargs.pop("short")]
170
+ else:
171
+ args = [long_name]
172
+
173
+ # this sets the key given back when args are parsed
174
+ kwargs["dest"] = field.name
175
+
176
+ if "default" in kwargs:
177
+ raise TypeError("'default' cannot be provided in field metadata")
178
+
179
+ if field.default != dataclasses.MISSING:
180
+ kwargs["default"] = field.default
181
+
182
+ if field.default_factory != dataclasses.MISSING:
183
+ kwargs["default"] = field.default_factory()
184
+
185
+ # when store true or store false is set, setting the type raises a type error
186
+ if kwargs.get("action") not in ("store_true", "store_false"):
187
+
188
+ # sometimes the user will set the type manually
189
+ # this is required for types such as `Option[T]`, where they cannt be directly constructed from a string
190
+ if "type" not in kwargs:
191
+ opts = get_type_args(field.type)
192
+
193
+ # set all of the options, except where already set
194
+ for key, value in opts.items():
195
+ if key not in kwargs:
196
+ kwargs[key] = value
197
+
198
+ parser.add_argument(*args, **kwargs)
199
+
200
+ @classmethod
201
+ def parse(cls: Type[T], program_name: str, section: str) -> T:
202
+ parser = ArgParser(
203
+ program_name,
204
+ formatter_class=ArgumentDefaultsHelpFormatter,
205
+ config_file_parser_class=TomlConfigParser(sections=[section]),
206
+ )
207
+
208
+ parser.add_argument("--config", "-c", is_config_file=True, help="Path to the TOML configuration file.")
209
+ cls.configure_parser(parser)
210
+
211
+ kwargs = vars(parser.parse_args())
212
+
213
+ # remove this from the args
214
+ kwargs.pop("config")
215
+
216
+ # we need to manually handle any ConfigClass fields
217
+ for field in dataclasses.fields(cls): # type: ignore[arg-type]
218
+ if is_config_class(field.type):
219
+
220
+ # steal arguments for the config class
221
+ inner_kwargs = {}
222
+ for f in dataclasses.fields(field.type): # type: ignore[arg-type]
223
+ if f.name in kwargs:
224
+ inner_kwargs[f.name] = kwargs.pop(f.name)
225
+
226
+ # instantiate and update the args
227
+ kwargs[field.name] = field.type(**inner_kwargs) # type: ignore[operator]
228
+
229
+ return cls(**kwargs)
230
+
231
+
232
+ def parse_bool(s: str) -> bool:
233
+ """parse a bool from a conventional string representation"""
234
+
235
+ lower = s.lower()
236
+ if lower == "true":
237
+ return True
238
+ if lower == "false":
239
+ return False
240
+
241
+ raise TypeError(f"[{s}] is not a valid bool")
242
+
243
+
244
+ def is_optional(ty: Any) -> bool:
245
+ """determines if `ty` is typing.Optional"""
246
+ return typing.get_origin(ty) is typing.Union and typing.get_args(ty)[1] is type(None)
247
+
248
+
249
+ def get_optional_type(ty: Any) -> type:
250
+ """get the `T` from a typing.Optional[T]"""
251
+ return typing.get_args(ty)[0]
252
+
253
+
254
+ def is_list(ty: Any) -> bool:
255
+ """determines if `ty` is typing.List or list"""
256
+ return typing.get_origin(ty) is list or ty is list
257
+
258
+
259
+ def get_list_type(ty: Any) -> type:
260
+ """get the generic type of a typing.List[T] or list[T]"""
261
+ return typing.get_args(ty)[0]
262
+
263
+
264
+ def is_config_type(ty: Any) -> bool:
265
+ """determines if ty is a subclass of ConfigType"""
266
+ try:
267
+ return issubclass(ty, ConfigType)
268
+ except TypeError:
269
+ return False
270
+
271
+
272
+ def is_config_class(ty: Any) -> bool:
273
+ """determines if ty is a subclass of ConfigClass"""
274
+ try:
275
+ return issubclass(ty, ConfigClass)
276
+ except TypeError:
277
+ return False
278
+
279
+
280
+ def get_type_args(ty: Any) -> Dict[str, Any]:
281
+ """
282
+ The type of a field implies several options for its argument parsing,
283
+ such as `type`, `nargs`, and `required`
284
+
285
+ For example a parameter of type Option[T] is parsed as `T`,
286
+ has no implication on `nargs`, and is not required
287
+
288
+ Similarly a parameter of List[T] is also parsed as `T`,
289
+ might have `nargs="*"`, and has no implication on `required`
290
+
291
+ This function determines these settings based upon a given type.
292
+ """
293
+
294
+ # bools have special parsing so that they behave as users expect
295
+ if ty is bool:
296
+ return {"type": parse_bool}
297
+
298
+ # for subclasses of ConfigType, we use the .from_string() method
299
+ if is_config_type(ty):
300
+ return {"type": ty.from_string}
301
+
302
+ # recursing handles the case where e.g. the inner type is a bool
303
+ # or a subclass of ConfigType, both of which need special handling
304
+ #
305
+ # parameters with this type are optional so we set `required=False`
306
+ if is_optional(ty):
307
+ opts = get_type_args(get_optional_type(ty))
308
+ return {"type": opts["type"], "required": False}
309
+
310
+ # `nargs="*"` is a reasonable default for lists that be overriden by the user
311
+ # if other behaviour is desired
312
+ if is_list(ty):
313
+ opts = get_type_args(get_list_type(ty))
314
+ return {"type": opts["type"], "nargs": "*"}
315
+
316
+ # the default, just use the type as-is
317
+ return {"type": ty}
@@ -0,0 +1,94 @@
1
+ import os
2
+
3
+ from scaler.config.types.network_backend import NetworkBackend
4
+
5
+ # ==============
6
+ # SYSTEM OPTIONS
7
+
8
+ # object clean up time interval
9
+ CLEANUP_INTERVAL_SECONDS = 1
10
+
11
+ # status report interval, used by poke or scaled monitor
12
+ STATUS_REPORT_INTERVAL_SECONDS = 1
13
+
14
+ # number of seconds for profiling
15
+ PROFILING_INTERVAL_SECONDS = 1
16
+
17
+ # cap'n proto only allow Data/Text/Blob size to be as big as 500MB
18
+ CAPNP_DATA_SIZE_LIMIT = 2**29 - 1
19
+
20
+ # message size limitation, max can be 2**64
21
+ CAPNP_MESSAGE_SIZE_LIMIT = 2**64 - 1
22
+
23
+ # ==========================
24
+ # SCHEDULER SPECIFIC OPTIONS
25
+
26
+ # number of threads for zmq socket to handle
27
+ DEFAULT_IO_THREADS = 1
28
+
29
+ # if all workers are full and busy working, this option determine how many additional tasks scheduler can receive and
30
+ # queued, if additional number of tasks received exceeded this number, scheduler will reject tasks
31
+ DEFAULT_MAX_NUMBER_OF_TASKS_WAITING = -1
32
+
33
+ # if didn't receive heartbeat for following seconds, then scheduler will treat worker as dead and reschedule unfinished
34
+ # tasks for this worker
35
+ DEFAULT_WORKER_TIMEOUT_SECONDS = 60
36
+
37
+ # if didn't receive heartbeat for following seconds, then scheduler will treat client as dead and cancel remaining
38
+ # tasks for this client
39
+ DEFAULT_CLIENT_TIMEOUT_SECONDS = 60
40
+
41
+ # number of seconds for load balance, if value is -1 means disable load balance
42
+ DEFAULT_LOAD_BALANCE_SECONDS = 1
43
+
44
+ # when load balance advice happened repeatedly and always be the same, we issue load balance request when exact repeated
45
+ # times happened
46
+ DEFAULT_LOAD_BALANCE_TRIGGER_TIMES = 2
47
+
48
+ # number of tasks can be queued to each worker on scheduler side
49
+ DEFAULT_PER_WORKER_QUEUE_SIZE = 1000
50
+
51
+ # =======================
52
+ # WORKER SPECIFIC OPTIONS
53
+
54
+ # number of workers, echo worker use 1 process
55
+ DEFAULT_NUMBER_OF_WORKER = os.cpu_count() - 1
56
+
57
+ # number of seconds that worker agent send heartbeat to scheduler
58
+ DEFAULT_HEARTBEAT_INTERVAL_SECONDS = 2
59
+
60
+ # number of seconds the object cache kept in worker's memory
61
+ DEFAULT_OBJECT_RETENTION_SECONDS = 60
62
+
63
+ # number of seconds worker doing garbage collection
64
+ DEFAULT_GARBAGE_COLLECT_INTERVAL_SECONDS = 30
65
+
66
+ # number of bytes threshold for worker process that trigger deep garbage collection
67
+ DEFAULT_TRIM_MEMORY_THRESHOLD_BYTES = 1024 * 1024 * 1024
68
+
69
+ # default task timeout seconds, 0 means never timeout
70
+ DEFAULT_TASK_TIMEOUT_SECONDS = 0
71
+
72
+ # number of seconds that worker agent wait for processor to finish before killing it
73
+ DEFAULT_PROCESSOR_KILL_DELAY_SECONDS = 3
74
+
75
+ # number of seconds without scheduler contact before worker shuts down
76
+ DEFAULT_WORKER_DEATH_TIMEOUT = 5 * 60
77
+
78
+ # if true, suspended worker's processors will be actively suspended with a SIGTSTP signal, otherwise a synchronization
79
+ # event will be used.
80
+ DEFAULT_HARD_PROCESSOR_SUSPEND = False
81
+
82
+ # =======================
83
+ # LOGGING SPECIFIC OPTIONS
84
+
85
+ # default logging level
86
+ DEFAULT_LOGGING_LEVEL = "INFO"
87
+
88
+ # default logging paths
89
+ DEFAULT_LOGGING_PATHS = ("/dev/stdout",)
90
+
91
+ # =======================
92
+ # SCALER NETWORK BACKEND SPECIFIC OPTIONS
93
+
94
+ SCALER_NETWORK_BACKEND = NetworkBackend.tcp_zmq
@@ -0,0 +1,20 @@
1
+ import abc
2
+ import sys
3
+
4
+ if sys.version_info >= (3, 11):
5
+ from typing import Self
6
+ else:
7
+ from typing_extensions import Self
8
+
9
+
10
+ class ConfigType(metaclass=abc.ABCMeta):
11
+ """A base class for composite config values that can be parsed and serialized from/to a string."""
12
+
13
+ @classmethod
14
+ @abc.abstractmethod
15
+ def from_string(cls, value: str) -> Self:
16
+ pass
17
+
18
+ @abc.abstractmethod
19
+ def __str__(self) -> str:
20
+ pass
File without changes
@@ -0,0 +1,66 @@
1
+ import dataclasses
2
+ import socket
3
+ from typing import Optional
4
+
5
+ from scaler.config import defaults
6
+ from scaler.config.common.logging import LoggingConfig
7
+ from scaler.config.common.worker import WorkerConfig
8
+ from scaler.config.config_class import ConfigClass
9
+ from scaler.config.types.object_storage_server import ObjectStorageAddressConfig
10
+ from scaler.config.types.worker import WorkerNames
11
+ from scaler.config.types.zmq import ZMQConfig
12
+ from scaler.utility.event_loop import EventLoopType
13
+
14
+
15
+ @dataclasses.dataclass
16
+ class ClusterConfig(ConfigClass):
17
+ scheduler_address: ZMQConfig = dataclasses.field(
18
+ metadata=dict(positional=True, help="the scheduler address to connect to")
19
+ )
20
+ object_storage_address: Optional[ObjectStorageAddressConfig] = dataclasses.field(
21
+ default=None,
22
+ metadata=dict(
23
+ short="-osa",
24
+ help=(
25
+ "the object storage server address, e.g. tcp://localhost:2346. "
26
+ "if not specified, uses the address provided by the scheduler"
27
+ ),
28
+ ),
29
+ )
30
+ preload: Optional[str] = dataclasses.field(
31
+ default=None,
32
+ metadata=dict(
33
+ help='optional module init in the form "pkg.mod:func(arg1, arg2)" executed in each processor before tasks'
34
+ ),
35
+ )
36
+ worker_names: WorkerNames = dataclasses.field(
37
+ default_factory=WorkerNames,
38
+ metadata=dict(
39
+ short="-wn", help="a comma-separated list of worker names to replace default worker names (host names)"
40
+ ),
41
+ )
42
+ num_of_workers: int = dataclasses.field(
43
+ default=defaults.DEFAULT_NUMBER_OF_WORKER, metadata=dict(short="-n", help="the number of workers in cluster")
44
+ )
45
+ event_loop: str = dataclasses.field(
46
+ default="builtin",
47
+ metadata=dict(short="-el", choices=EventLoopType.allowed_types(), help="select the event loop type"),
48
+ )
49
+
50
+ worker_io_threads: int = dataclasses.field(
51
+ default=defaults.DEFAULT_IO_THREADS,
52
+ metadata=dict(short="-wit", help="set the number of io threads for io backend per worker"),
53
+ )
54
+ worker_config: WorkerConfig = dataclasses.field(default_factory=WorkerConfig)
55
+ logging_config: LoggingConfig = dataclasses.field(default_factory=LoggingConfig)
56
+
57
+ def __post_init__(self):
58
+ if self.worker_names.names and len(self.worker_names.names) != self.num_of_workers:
59
+ raise ValueError(
60
+ f"the number of worker_names ({len(self.worker_names.names)}) "
61
+ "must match num_of_workers ({self.num_of_workers})."
62
+ )
63
+ if not self.worker_names.names:
64
+ self.worker_names.names = [f"{socket.gethostname().split('.')[0]}" for _ in range(self.num_of_workers)]
65
+ if self.worker_io_threads <= 0:
66
+ raise ValueError("worker_io_threads must be a positive integer.")
@@ -0,0 +1,78 @@
1
+ import dataclasses
2
+ from typing import List, Optional
3
+
4
+ from scaler.config import defaults
5
+ from scaler.config.common.logging import LoggingConfig
6
+ from scaler.config.common.web import WebConfig
7
+ from scaler.config.common.worker import WorkerConfig
8
+ from scaler.config.common.worker_adapter import WorkerAdapterConfig
9
+ from scaler.config.config_class import ConfigClass
10
+ from scaler.utility.event_loop import EventLoopType
11
+
12
+
13
+ @dataclasses.dataclass
14
+ class ECSWorkerAdapterConfig(ConfigClass):
15
+ web_config: WebConfig
16
+ worker_adapter_config: WorkerAdapterConfig
17
+ worker_config: WorkerConfig = dataclasses.field(default_factory=WorkerConfig)
18
+ logging_config: LoggingConfig = dataclasses.field(default_factory=LoggingConfig)
19
+ event_loop: str = dataclasses.field(
20
+ default="builtin",
21
+ metadata=dict(short="-el", choices=EventLoopType.allowed_types(), help="select the event loop type"),
22
+ )
23
+
24
+ worker_io_threads: int = dataclasses.field(
25
+ default=defaults.DEFAULT_IO_THREADS,
26
+ metadata=dict(short="-wit", help="set the number of io threads for io backend per worker"),
27
+ )
28
+
29
+ # AWS / ECS specific configuration
30
+ aws_access_key_id: Optional[str] = dataclasses.field(
31
+ default=None, metadata=dict(env_var="AWS_ACCESS_KEY_ID", help="AWS access key id")
32
+ )
33
+ aws_secret_access_key: Optional[str] = dataclasses.field(
34
+ default=None, metadata=dict(env_var="AWS_SECRET_ACCESS_KEY", help="AWS secret access key")
35
+ )
36
+ aws_region: str = dataclasses.field(default="us-east-1", metadata=dict(help="AWS region for ECS cluster"))
37
+ ecs_subnets: List[str] = dataclasses.field(
38
+ default_factory=list,
39
+ metadata=dict(
40
+ type=lambda s: [x for x in s.split(",") if x],
41
+ required=True,
42
+ help="Comma-separated list of AWS subnet IDs for ECS tasks",
43
+ ),
44
+ )
45
+ ecs_cluster: str = dataclasses.field(default="scaler-cluster", metadata=dict(help="ECS cluster name"))
46
+ ecs_task_image: str = dataclasses.field(
47
+ default="public.ecr.aws/v4u8j8r6/scaler:latest", metadata=dict(help="Container image used for ECS tasks")
48
+ )
49
+ ecs_python_requirements: str = dataclasses.field(
50
+ default="tomli;pargraph;parfun;pandas", metadata=dict(help="Python requirements string passed to the ECS task")
51
+ )
52
+ ecs_python_version: str = dataclasses.field(default="3.12.11", metadata=dict(help="Python version for ECS task"))
53
+ ecs_task_definition: str = dataclasses.field(
54
+ default="scaler-task-definition", metadata=dict(help="ECS task definition")
55
+ )
56
+ ecs_task_cpu: int = dataclasses.field(
57
+ default=4, metadata=dict(help="Number of vCPUs for task (used to derive worker count)")
58
+ )
59
+ ecs_task_memory: int = dataclasses.field(default=30, metadata=dict(help="Task memory in GB for Fargate"))
60
+
61
+ def __post_init__(self):
62
+ # Validate numeric and collection values
63
+ if self.ecs_task_cpu <= 0:
64
+ raise ValueError("ecs_task_cpu must be a positive integer.")
65
+ if self.ecs_task_memory <= 0:
66
+ raise ValueError("ecs_task_memory must be a positive integer.")
67
+ if not isinstance(self.ecs_subnets, list) or len(self.ecs_subnets) == 0:
68
+ raise ValueError("ecs_subnets must be a non-empty list of subnet ids.")
69
+
70
+ # Validate required strings
71
+ if not self.ecs_cluster:
72
+ raise ValueError("ecs_cluster cannot be an empty string.")
73
+ if not self.ecs_task_definition:
74
+ raise ValueError("ecs_task_definition cannot be an empty string.")
75
+ if not self.ecs_task_image:
76
+ raise ValueError("ecs_task_image cannot be an empty string.")
77
+ if self.worker_io_threads <= 0:
78
+ raise ValueError("worker_io_threads must be a positive integer.")
@@ -0,0 +1,30 @@
1
+ import dataclasses
2
+
3
+ from scaler.config import defaults
4
+ from scaler.config.common.logging import LoggingConfig
5
+ from scaler.config.common.web import WebConfig
6
+ from scaler.config.common.worker import WorkerConfig
7
+ from scaler.config.common.worker_adapter import WorkerAdapterConfig
8
+ from scaler.config.config_class import ConfigClass
9
+ from scaler.utility.event_loop import EventLoopType
10
+
11
+
12
+ @dataclasses.dataclass
13
+ class NativeWorkerAdapterConfig(ConfigClass):
14
+ web_config: WebConfig
15
+ worker_adapter_config: WorkerAdapterConfig
16
+ worker_config: WorkerConfig = dataclasses.field(default_factory=WorkerConfig)
17
+ logging_config: LoggingConfig = dataclasses.field(default_factory=LoggingConfig)
18
+ event_loop: str = dataclasses.field(
19
+ default="builtin",
20
+ metadata=dict(short="-el", choices=EventLoopType.allowed_types(), help="select the event loop type"),
21
+ )
22
+
23
+ worker_io_threads: int = dataclasses.field(
24
+ default=defaults.DEFAULT_IO_THREADS,
25
+ metadata=dict(short="-wit", help="set the number of io threads for io backend per worker"),
26
+ )
27
+
28
+ def __post_init__(self) -> None:
29
+ if self.worker_io_threads <= 0:
30
+ raise ValueError("worker_io_threads must be a positive integer.")
@@ -0,0 +1,13 @@
1
+ import dataclasses
2
+
3
+ from scaler.config.config_class import ConfigClass
4
+ from scaler.config.types.object_storage_server import ObjectStorageAddressConfig
5
+
6
+
7
+ @dataclasses.dataclass
8
+ class ObjectStorageServerConfig(ConfigClass):
9
+ object_storage_address: ObjectStorageAddressConfig = dataclasses.field(
10
+ metadata=dict(
11
+ positional=True, help="specify the object storage server address to listen to, e.g. tcp://localhost:2345."
12
+ )
13
+ )