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,126 @@
1
+ import dataclasses
2
+ from typing import Optional, Tuple
3
+ from urllib.parse import urlparse
4
+
5
+ from scaler.config import defaults
6
+ from scaler.config.common.logging import LoggingConfig
7
+ from scaler.config.config_class import ConfigClass
8
+ from scaler.config.types.object_storage_server import ObjectStorageAddressConfig
9
+ from scaler.config.types.zmq import ZMQConfig
10
+ from scaler.scheduler.allocate_policy.allocate_policy import AllocatePolicy
11
+ from scaler.scheduler.controllers.scaling_policies.types import ScalingControllerStrategy
12
+ from scaler.utility.event_loop import EventLoopType
13
+
14
+
15
+ @dataclasses.dataclass
16
+ class SchedulerConfig(ConfigClass):
17
+ scheduler_address: ZMQConfig = dataclasses.field(
18
+ metadata=dict(positional=True, help="scheduler address to connect to, e.g.: `tcp://localhost:6378`")
19
+ )
20
+ object_storage_address: Optional[ObjectStorageAddressConfig] = dataclasses.field(
21
+ default=None,
22
+ metadata=dict(
23
+ short="-osa",
24
+ help="specify the object storage server address, if not specified, "
25
+ "the address is scheduler address with port number plus 1, "
26
+ "e.g.: if scheduler address is tcp://localhost:2345, "
27
+ "then object storage address is tcp://localhost:2346",
28
+ ),
29
+ )
30
+ monitor_address: Optional[ZMQConfig] = dataclasses.field(
31
+ default=None,
32
+ metadata=dict(
33
+ short="-ma",
34
+ help="specify monitoring address, if not specified, the monitoring address is scheduler address with port "
35
+ "number plus 2, e.g.: if scheduler address is tcp://localhost:2345, then monitoring address is "
36
+ "tcp://localhost:2347",
37
+ ),
38
+ )
39
+ scaling_controller_strategy: ScalingControllerStrategy = dataclasses.field(
40
+ default=ScalingControllerStrategy.NULL,
41
+ metadata=dict(
42
+ short="-scs",
43
+ choices=[s.name for s in ScalingControllerStrategy],
44
+ help="specify the scaling controller strategy, if not specified, no scaling controller will be used",
45
+ ),
46
+ )
47
+ adapter_webhook_urls: Tuple[str, ...] = dataclasses.field(
48
+ default=(),
49
+ metadata=dict(
50
+ short="-awu",
51
+ type=str,
52
+ nargs="*",
53
+ help="specify the adapter webhook urls for the scaling controller to send scaling events to",
54
+ ),
55
+ )
56
+ protected: bool = dataclasses.field(
57
+ default=False,
58
+ metadata=dict(
59
+ short="-p", action="store_true", help="protect scheduler and worker from being shutdown by client"
60
+ ),
61
+ )
62
+ allocate_policy: AllocatePolicy = dataclasses.field(
63
+ default=AllocatePolicy.even,
64
+ metadata=dict(
65
+ short="-ap",
66
+ choices=[p.name for p in AllocatePolicy],
67
+ help="specify allocate policy, this controls how scheduler will prioritize tasks, "
68
+ "including balancing tasks",
69
+ ),
70
+ )
71
+ max_number_of_tasks_waiting: int = dataclasses.field(
72
+ default=defaults.DEFAULT_MAX_NUMBER_OF_TASKS_WAITING,
73
+ metadata=dict(short="-mt", help="max number of tasks can wait in scheduler while all workers are full"),
74
+ )
75
+ client_timeout_seconds: int = dataclasses.field(
76
+ default=defaults.DEFAULT_CLIENT_TIMEOUT_SECONDS,
77
+ metadata=dict(short="-ct", help="discard client when timeout seconds reached"),
78
+ )
79
+ worker_timeout_seconds: int = dataclasses.field(
80
+ default=defaults.DEFAULT_WORKER_TIMEOUT_SECONDS,
81
+ metadata=dict(short="-wt", help="discard worker when timeout seconds reached"),
82
+ )
83
+ object_retention_seconds: int = dataclasses.field(
84
+ default=defaults.DEFAULT_OBJECT_RETENTION_SECONDS,
85
+ metadata=dict(short="-ot", help="discard function in scheduler when timeout seconds reached"),
86
+ )
87
+ load_balance_seconds: int = dataclasses.field(
88
+ default=defaults.DEFAULT_LOAD_BALANCE_SECONDS,
89
+ metadata=dict(short="-ls", help="number of seconds for load balance operation in scheduler"),
90
+ )
91
+ load_balance_trigger_times: int = dataclasses.field(
92
+ default=defaults.DEFAULT_LOAD_BALANCE_TRIGGER_TIMES,
93
+ metadata=dict(
94
+ short="-lbt",
95
+ help="exact number of repeated load balance advices when trigger load balance operation in scheduler",
96
+ ),
97
+ )
98
+ event_loop: str = dataclasses.field(
99
+ default="builtin",
100
+ metadata=dict(short="-el", choices=EventLoopType.allowed_types(), help="select the event loop type"),
101
+ )
102
+
103
+ worker_io_threads: int = dataclasses.field(
104
+ default=defaults.DEFAULT_IO_THREADS,
105
+ metadata=dict(short="-wit", help="set the number of io threads for io backend per worker"),
106
+ )
107
+ logging_config: LoggingConfig = dataclasses.field(default_factory=LoggingConfig)
108
+
109
+ def __post_init__(self):
110
+ if self.max_number_of_tasks_waiting < -1:
111
+ raise ValueError("max_number_of_tasks_waiting must be -1 (for unlimited) or non-negative.")
112
+ if (
113
+ self.client_timeout_seconds <= 0
114
+ or self.worker_timeout_seconds <= 0
115
+ or self.object_retention_seconds <= 0
116
+ or self.load_balance_seconds <= 0
117
+ ):
118
+ raise ValueError("All timeout/retention/balance second values must be positive.")
119
+ if self.load_balance_trigger_times <= 0:
120
+ raise ValueError("load_balance_trigger_times must be a positive integer.")
121
+ for adapter_webhook_url in self.adapter_webhook_urls:
122
+ parsed_url = urlparse(adapter_webhook_url)
123
+ if not all([parsed_url.scheme, parsed_url.netloc]):
124
+ raise ValueError(f"adapter_webhook_urls contains url '{adapter_webhook_url}' which is not a valid URL.")
125
+ if self.worker_io_threads <= 0:
126
+ raise ValueError("worker_io_threads must be a positive integer.")
@@ -0,0 +1,35 @@
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 SymphonyWorkerConfig(ConfigClass):
14
+ service_name: str = dataclasses.field(metadata=dict(short="-sn", help="symphony service name"))
15
+
16
+ web_config: WebConfig
17
+ worker_adapter_config: WorkerAdapterConfig
18
+ worker_config: WorkerConfig = dataclasses.field(default_factory=WorkerConfig)
19
+ logging_config: LoggingConfig = dataclasses.field(default_factory=LoggingConfig)
20
+ event_loop: str = dataclasses.field(
21
+ default="builtin",
22
+ metadata=dict(short="-el", choices=EventLoopType.allowed_types(), help="select the event loop type"),
23
+ )
24
+
25
+ worker_io_threads: int = dataclasses.field(
26
+ default=defaults.DEFAULT_IO_THREADS,
27
+ metadata=dict(short="-wit", help="set the number of io threads for io backend per worker"),
28
+ )
29
+
30
+ def __post_init__(self):
31
+ """Validates configuration values after initialization."""
32
+ if not self.service_name:
33
+ raise ValueError("service_name cannot be an empty string.")
34
+ if self.worker_io_threads <= 0:
35
+ raise ValueError("worker_io_threads must be a positive integer.")
@@ -0,0 +1,16 @@
1
+ import dataclasses
2
+
3
+ from scaler.config.config_class import ConfigClass
4
+ from scaler.config.types.zmq import ZMQConfig
5
+
6
+
7
+ @dataclasses.dataclass
8
+ class TopConfig(ConfigClass):
9
+ monitor_address: ZMQConfig = dataclasses.field(
10
+ metadata=dict(positional=True, help="scheduler monitor address to connect to")
11
+ )
12
+ timeout: int = dataclasses.field(default=5, metadata=dict(short="-t", help="timeout seconds"))
13
+
14
+ def __post_init__(self):
15
+ if self.timeout <= 0:
16
+ raise ValueError("timeout must be a positive integer.")
@@ -0,0 +1,16 @@
1
+ import dataclasses
2
+
3
+ from scaler.config.common.logging import LoggingConfig
4
+ from scaler.config.config_class import ConfigClass
5
+ from scaler.config.types.zmq import ZMQConfig
6
+
7
+
8
+ @dataclasses.dataclass
9
+ class WebUIConfig(ConfigClass):
10
+ monitor_address: ZMQConfig = dataclasses.field(
11
+ metadata=dict(positional=True, help="scheduler monitor address to connect to")
12
+ )
13
+ web_host: str = dataclasses.field(default="0.0.0.0", metadata=dict(help="host for webserver to connect to"))
14
+ web_port: int = dataclasses.field(default=50001, metadata=dict(help="host for webserver to connect to"))
15
+
16
+ logging_config: LoggingConfig = dataclasses.field(default_factory=LoggingConfig)
File without changes
@@ -0,0 +1,12 @@
1
+ import enum
2
+
3
+
4
+ class NetworkBackend(enum.Enum):
5
+ """
6
+ Network backend to select when running scaler
7
+ - tcp_zmq means for oss it use raw tcp, for client/scheduler/worker communication it use zmq
8
+ - ymq means all components will use ymq for communication
9
+ """
10
+
11
+ tcp_zmq = enum.auto()
12
+ ymq = enum.auto()
@@ -0,0 +1,45 @@
1
+ import dataclasses
2
+ import ipaddress
3
+
4
+ from scaler.config.mixins import ConfigType
5
+
6
+
7
+ @dataclasses.dataclass
8
+ class ObjectStorageAddressConfig(ConfigType):
9
+ host: str
10
+ port: int
11
+ identity: str = "ObjectStorageServer"
12
+
13
+ def __post_init__(self):
14
+ try:
15
+ ipaddress.ip_address(self.host)
16
+ except ValueError:
17
+ raise TypeError(f"Host must be a valid IP address, but got '{self.host}'")
18
+
19
+ if not isinstance(self.identity, str):
20
+ raise TypeError(f"Identity should be a string, given {self.identity}")
21
+
22
+ if not isinstance(self.port, int):
23
+ raise TypeError(f"Port should be an integer, given {self.port}")
24
+
25
+ def __str__(self) -> str:
26
+ return self.to_string()
27
+
28
+ def to_string(self) -> str:
29
+ return f"tcp://{self.host}:{self.port}"
30
+
31
+ @classmethod
32
+ def from_string(cls, value: str) -> "ObjectStorageAddressConfig":
33
+ if not value.startswith("tcp://"):
34
+ raise ValueError("Address must start with 'tcp://'")
35
+
36
+ try:
37
+ host, port_str = value[6:].rsplit(":", 1)
38
+ port = int(port_str)
39
+
40
+ ipaddress.ip_address(host)
41
+
42
+ except (ValueError, IndexError):
43
+ raise ValueError(f"Invalid address format '{value}'. Expected format is tcp://<ip_address>:<port>")
44
+
45
+ return cls(host=host, port=port)
@@ -0,0 +1,67 @@
1
+ import dataclasses
2
+ import sys
3
+ from typing import Dict, List
4
+
5
+ if sys.version_info >= (3, 11):
6
+ from typing import Self
7
+ else:
8
+ from typing_extensions import Self
9
+
10
+ from scaler.config.mixins import ConfigType
11
+
12
+
13
+ @dataclasses.dataclass
14
+ class WorkerNames(ConfigType):
15
+ """Parses a comma-separated string of worker names into a list."""
16
+
17
+ names: List[str] = dataclasses.field(default_factory=list)
18
+
19
+ @classmethod
20
+ def from_string(cls, value: str) -> Self:
21
+ if not value:
22
+ return cls([])
23
+ names = [name.strip() for name in value.split(",")]
24
+ return cls(names)
25
+
26
+ def __str__(self) -> str:
27
+ if self.names:
28
+ return ",".join(self.names)
29
+ return "<empty>"
30
+
31
+ def __len__(self) -> int:
32
+ return len(self.names)
33
+
34
+
35
+ @dataclasses.dataclass
36
+ class WorkerCapabilities(ConfigType):
37
+ """Parses a string of worker capabilities."""
38
+
39
+ capabilities: Dict[str, int] = dataclasses.field(default_factory=dict)
40
+
41
+ @classmethod
42
+ def from_string(cls, value: str) -> Self:
43
+ capabilities: Dict[str, int] = {}
44
+ if not value:
45
+ return cls(capabilities)
46
+ for item in value.split(","):
47
+ name, _, value = item.partition("=")
48
+ if value != "":
49
+ try:
50
+ capabilities[name.strip()] = int(value)
51
+ except ValueError:
52
+ raise ValueError(f"Invalid capability value for '{name}'. Expected an integer, but got '{value}'.")
53
+ else:
54
+ capabilities[name.strip()] = -1
55
+ return cls(capabilities)
56
+
57
+ def __str__(self) -> str:
58
+ if not self.capabilities:
59
+ return "<empty>"
60
+
61
+ items = []
62
+ for name, cap in self.capabilities.items():
63
+ if cap == -1:
64
+ items.append(name)
65
+ else:
66
+ items.append(f"{name}={cap}")
67
+ return ",".join(items)
@@ -0,0 +1,83 @@
1
+ import dataclasses
2
+ import enum
3
+ import sys
4
+ from typing import Optional
5
+
6
+ if sys.version_info >= (3, 11):
7
+ from typing import Self
8
+ else:
9
+ from typing_extensions import Self
10
+ from scaler.config.mixins import ConfigType
11
+
12
+
13
+ class ZMQType(enum.Enum):
14
+ inproc = "inproc"
15
+ ipc = "ipc"
16
+ tcp = "tcp"
17
+
18
+ @staticmethod
19
+ def allowed_types():
20
+ return {t.value for t in ZMQType}
21
+
22
+
23
+ @dataclasses.dataclass
24
+ class ZMQConfig(ConfigType):
25
+ type: ZMQType
26
+ host: str
27
+ port: Optional[int] = None
28
+
29
+ def __post_init__(self):
30
+ if not isinstance(self.type, ZMQType):
31
+ raise TypeError(f"Invalid zmq type {self.type}, available types are: {ZMQType.allowed_types()}")
32
+
33
+ if not isinstance(self.host, str):
34
+ raise TypeError(f"Host should be string, given {self.host}")
35
+
36
+ if self.port is None:
37
+ if self.type == ZMQType.tcp:
38
+ raise ValueError(f"type {self.type.value} should have `port`")
39
+ else:
40
+ if self.type in {ZMQType.inproc, ZMQType.ipc}:
41
+ raise ValueError(f"type {self.type.value} should not have `port`")
42
+
43
+ if not isinstance(self.port, int):
44
+ raise TypeError(f"Port should be integer, given {self.port}")
45
+
46
+ def to_address(self):
47
+ if self.type == ZMQType.tcp:
48
+ return f"tcp://{self.host}:{self.port}"
49
+
50
+ if self.type in {ZMQType.inproc, ZMQType.ipc}:
51
+ return f"{self.type.value}://{self.host}"
52
+
53
+ raise TypeError(f"Unsupported ZMQ type: {self.type}")
54
+
55
+ @classmethod
56
+ def from_string(cls, value: str) -> Self:
57
+ if "://" not in value:
58
+ raise ValueError("valid ZMQ config should be like tcp://127.0.0.1:12345")
59
+
60
+ socket_type, host_port = value.split("://", 1)
61
+ if socket_type not in ZMQType.allowed_types():
62
+ raise ValueError(f"supported ZMQ types are: {ZMQType.allowed_types()}")
63
+
64
+ socket_type_enum = ZMQType(socket_type)
65
+ if socket_type_enum in {ZMQType.inproc, ZMQType.ipc}:
66
+ host = host_port
67
+ port_int = None
68
+ elif socket_type_enum == ZMQType.tcp:
69
+ host, port = host_port.split(":")
70
+ try:
71
+ port_int = int(port)
72
+ except ValueError:
73
+ raise ValueError(f"cannot convert '{port}' to port number")
74
+ else:
75
+ raise ValueError(f"Unsupported ZMQ type: {socket_type}")
76
+
77
+ return cls(socket_type_enum, host, port_int)
78
+
79
+ def __str__(self) -> str:
80
+ return self.to_address()
81
+
82
+ def __repr__(self) -> str:
83
+ return self.to_address()
File without changes
@@ -0,0 +1,10 @@
1
+ from scaler.cluster.cluster import Cluster
2
+ from scaler.config.section.cluster import ClusterConfig
3
+ from scaler.utility.event_loop import register_event_loop
4
+
5
+
6
+ def main():
7
+ cluster_config = ClusterConfig.parse("Scaler Standalone Compute Cluster", "cluster")
8
+ register_event_loop(cluster_config.event_loop)
9
+ cluster = Cluster(cluster_config)
10
+ cluster.run()
@@ -0,0 +1,26 @@
1
+ import logging
2
+ import sys
3
+
4
+ from scaler.config.section.object_storage_server import ObjectStorageServerConfig
5
+ from scaler.object_storage.object_storage_server import ObjectStorageServer
6
+ from scaler.utility.logging.utility import get_logger_info, setup_logger
7
+
8
+
9
+ def main():
10
+ oss_config = ObjectStorageServerConfig.parse("Scaler Object Storage Server", "object_storage_server")
11
+
12
+ setup_logger()
13
+
14
+ log_format_str, log_level_str, log_paths = get_logger_info(logging.getLogger())
15
+
16
+ try:
17
+ ObjectStorageServer().run(
18
+ oss_config.object_storage_address.host,
19
+ oss_config.object_storage_address.port,
20
+ oss_config.object_storage_address.identity,
21
+ log_level_str,
22
+ log_format_str,
23
+ log_paths,
24
+ )
25
+ except KeyboardInterrupt:
26
+ sys.exit(0)
@@ -0,0 +1,51 @@
1
+ from scaler.cluster.object_storage_server import ObjectStorageServerProcess
2
+ from scaler.cluster.scheduler import SchedulerProcess
3
+ from scaler.config.section.scheduler import SchedulerConfig
4
+ from scaler.config.types.object_storage_server import ObjectStorageAddressConfig
5
+
6
+
7
+ def main():
8
+ scheduler_config = SchedulerConfig.parse("Scaler Scheduler", "scheduler")
9
+
10
+ object_storage_address = scheduler_config.object_storage_address
11
+ object_storage = None
12
+
13
+ if object_storage_address is None:
14
+ assert scheduler_config.scheduler_address.port is not None, "Scheduler address must have a port"
15
+ object_storage_address = ObjectStorageAddressConfig(
16
+ host=scheduler_config.scheduler_address.host, port=scheduler_config.scheduler_address.port + 1
17
+ )
18
+ object_storage = ObjectStorageServerProcess(
19
+ object_storage_address=object_storage_address,
20
+ logging_paths=scheduler_config.logging_config.paths,
21
+ logging_config_file=scheduler_config.logging_config.config_file,
22
+ logging_level=scheduler_config.logging_config.level,
23
+ )
24
+ object_storage.start()
25
+ object_storage.wait_until_ready() # object storage should be ready before starting the cluster
26
+
27
+ scheduler = SchedulerProcess(
28
+ address=scheduler_config.scheduler_address,
29
+ object_storage_address=object_storage_address,
30
+ monitor_address=scheduler_config.monitor_address,
31
+ scaling_controller_strategy=scheduler_config.scaling_controller_strategy,
32
+ adapter_webhook_urls=scheduler_config.adapter_webhook_urls,
33
+ io_threads=scheduler_config.worker_io_threads,
34
+ max_number_of_tasks_waiting=scheduler_config.max_number_of_tasks_waiting,
35
+ client_timeout_seconds=scheduler_config.client_timeout_seconds,
36
+ worker_timeout_seconds=scheduler_config.worker_timeout_seconds,
37
+ object_retention_seconds=scheduler_config.object_retention_seconds,
38
+ load_balance_seconds=scheduler_config.load_balance_seconds,
39
+ load_balance_trigger_times=scheduler_config.load_balance_trigger_times,
40
+ protected=scheduler_config.protected,
41
+ allocate_policy=scheduler_config.allocate_policy,
42
+ event_loop=scheduler_config.event_loop,
43
+ logging_paths=scheduler_config.logging_config.paths,
44
+ logging_config_file=scheduler_config.logging_config.config_file,
45
+ logging_level=scheduler_config.logging_config.level,
46
+ )
47
+ scheduler.start()
48
+
49
+ scheduler.join()
50
+ if object_storage is not None:
51
+ object_storage.join()