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.
- opengris_scaler-1.12.37.dist-info/METADATA +730 -0
- opengris_scaler-1.12.37.dist-info/RECORD +196 -0
- opengris_scaler-1.12.37.dist-info/WHEEL +5 -0
- opengris_scaler-1.12.37.dist-info/entry_points.txt +10 -0
- opengris_scaler-1.12.37.dist-info/licenses/LICENSE +201 -0
- opengris_scaler-1.12.37.dist-info/licenses/LICENSE.spdx +7 -0
- opengris_scaler-1.12.37.dist-info/licenses/NOTICE +8 -0
- opengris_scaler.libs/libcapnp-1-e88d5415.0.1.so +0 -0
- opengris_scaler.libs/libgcc_s-2298274a.so.1 +0 -0
- opengris_scaler.libs/libkj-1-9bebd8ac.0.1.so +0 -0
- opengris_scaler.libs/libstdc++-08d5c7eb.so.6.0.33 +0 -0
- scaler/__init__.py +14 -0
- scaler/about.py +5 -0
- scaler/client/__init__.py +0 -0
- scaler/client/agent/__init__.py +0 -0
- scaler/client/agent/client_agent.py +218 -0
- scaler/client/agent/disconnect_manager.py +27 -0
- scaler/client/agent/future_manager.py +112 -0
- scaler/client/agent/heartbeat_manager.py +74 -0
- scaler/client/agent/mixins.py +89 -0
- scaler/client/agent/object_manager.py +98 -0
- scaler/client/agent/task_manager.py +64 -0
- scaler/client/client.py +672 -0
- scaler/client/future.py +252 -0
- scaler/client/object_buffer.py +129 -0
- scaler/client/object_reference.py +25 -0
- scaler/client/serializer/__init__.py +0 -0
- scaler/client/serializer/default.py +16 -0
- scaler/client/serializer/mixins.py +38 -0
- scaler/cluster/__init__.py +0 -0
- scaler/cluster/cluster.py +95 -0
- scaler/cluster/combo.py +157 -0
- scaler/cluster/object_storage_server.py +45 -0
- scaler/cluster/scheduler.py +86 -0
- scaler/config/__init__.py +0 -0
- scaler/config/common/__init__.py +0 -0
- scaler/config/common/logging.py +41 -0
- scaler/config/common/web.py +18 -0
- scaler/config/common/worker.py +65 -0
- scaler/config/common/worker_adapter.py +28 -0
- scaler/config/config_class.py +317 -0
- scaler/config/defaults.py +94 -0
- scaler/config/mixins.py +20 -0
- scaler/config/section/__init__.py +0 -0
- scaler/config/section/cluster.py +66 -0
- scaler/config/section/ecs_worker_adapter.py +78 -0
- scaler/config/section/native_worker_adapter.py +30 -0
- scaler/config/section/object_storage_server.py +13 -0
- scaler/config/section/scheduler.py +126 -0
- scaler/config/section/symphony_worker_adapter.py +35 -0
- scaler/config/section/top.py +16 -0
- scaler/config/section/webui.py +16 -0
- scaler/config/types/__init__.py +0 -0
- scaler/config/types/network_backend.py +12 -0
- scaler/config/types/object_storage_server.py +45 -0
- scaler/config/types/worker.py +67 -0
- scaler/config/types/zmq.py +83 -0
- scaler/entry_points/__init__.py +0 -0
- scaler/entry_points/cluster.py +10 -0
- scaler/entry_points/object_storage_server.py +26 -0
- scaler/entry_points/scheduler.py +51 -0
- scaler/entry_points/top.py +272 -0
- scaler/entry_points/webui.py +6 -0
- scaler/entry_points/worker_adapter_ecs.py +22 -0
- scaler/entry_points/worker_adapter_native.py +31 -0
- scaler/entry_points/worker_adapter_symphony.py +26 -0
- scaler/io/__init__.py +0 -0
- scaler/io/async_binder.py +89 -0
- scaler/io/async_connector.py +95 -0
- scaler/io/async_object_storage_connector.py +225 -0
- scaler/io/mixins.py +154 -0
- scaler/io/sync_connector.py +68 -0
- scaler/io/sync_object_storage_connector.py +249 -0
- scaler/io/sync_subscriber.py +83 -0
- scaler/io/utility.py +80 -0
- scaler/io/ymq/__init__.py +0 -0
- scaler/io/ymq/_ymq.pyi +95 -0
- scaler/io/ymq/_ymq.so +0 -0
- scaler/io/ymq/ymq.py +138 -0
- scaler/io/ymq_async_object_storage_connector.py +184 -0
- scaler/io/ymq_sync_object_storage_connector.py +184 -0
- scaler/object_storage/__init__.py +0 -0
- scaler/object_storage/object_storage_server.so +0 -0
- scaler/protocol/__init__.py +0 -0
- scaler/protocol/capnp/__init__.py +0 -0
- scaler/protocol/capnp/_python.py +6 -0
- scaler/protocol/capnp/common.capnp +68 -0
- scaler/protocol/capnp/message.capnp +218 -0
- scaler/protocol/capnp/object_storage.capnp +57 -0
- scaler/protocol/capnp/status.capnp +73 -0
- scaler/protocol/introduction.md +105 -0
- scaler/protocol/python/__init__.py +0 -0
- scaler/protocol/python/common.py +140 -0
- scaler/protocol/python/message.py +751 -0
- scaler/protocol/python/mixins.py +13 -0
- scaler/protocol/python/object_storage.py +118 -0
- scaler/protocol/python/status.py +279 -0
- scaler/protocol/worker.md +228 -0
- scaler/scheduler/__init__.py +0 -0
- scaler/scheduler/allocate_policy/__init__.py +0 -0
- scaler/scheduler/allocate_policy/allocate_policy.py +9 -0
- scaler/scheduler/allocate_policy/capability_allocate_policy.py +280 -0
- scaler/scheduler/allocate_policy/even_load_allocate_policy.py +159 -0
- scaler/scheduler/allocate_policy/mixins.py +55 -0
- scaler/scheduler/controllers/__init__.py +0 -0
- scaler/scheduler/controllers/balance_controller.py +65 -0
- scaler/scheduler/controllers/client_controller.py +131 -0
- scaler/scheduler/controllers/config_controller.py +31 -0
- scaler/scheduler/controllers/graph_controller.py +424 -0
- scaler/scheduler/controllers/information_controller.py +81 -0
- scaler/scheduler/controllers/mixins.py +194 -0
- scaler/scheduler/controllers/object_controller.py +147 -0
- scaler/scheduler/controllers/scaling_policies/__init__.py +0 -0
- scaler/scheduler/controllers/scaling_policies/fixed_elastic.py +145 -0
- scaler/scheduler/controllers/scaling_policies/mixins.py +10 -0
- scaler/scheduler/controllers/scaling_policies/null.py +14 -0
- scaler/scheduler/controllers/scaling_policies/types.py +9 -0
- scaler/scheduler/controllers/scaling_policies/utility.py +20 -0
- scaler/scheduler/controllers/scaling_policies/vanilla.py +95 -0
- scaler/scheduler/controllers/task_controller.py +376 -0
- scaler/scheduler/controllers/worker_controller.py +169 -0
- scaler/scheduler/object_usage/__init__.py +0 -0
- scaler/scheduler/object_usage/object_tracker.py +131 -0
- scaler/scheduler/scheduler.py +251 -0
- scaler/scheduler/task/__init__.py +0 -0
- scaler/scheduler/task/task_state_machine.py +92 -0
- scaler/scheduler/task/task_state_manager.py +61 -0
- scaler/ui/__init__.py +0 -0
- scaler/ui/common/__init__.py +0 -0
- scaler/ui/common/constants.py +9 -0
- scaler/ui/common/live_display.py +147 -0
- scaler/ui/common/memory_window.py +146 -0
- scaler/ui/common/setting_page.py +40 -0
- scaler/ui/common/task_graph.py +840 -0
- scaler/ui/common/task_log.py +111 -0
- scaler/ui/common/utility.py +66 -0
- scaler/ui/common/webui.py +80 -0
- scaler/ui/common/worker_processors.py +104 -0
- scaler/ui/v1.py +76 -0
- scaler/ui/v2.py +102 -0
- scaler/ui/webui.py +21 -0
- scaler/utility/__init__.py +0 -0
- scaler/utility/debug.py +19 -0
- scaler/utility/event_list.py +63 -0
- scaler/utility/event_loop.py +58 -0
- scaler/utility/exceptions.py +42 -0
- scaler/utility/formatter.py +44 -0
- scaler/utility/graph/__init__.py +0 -0
- scaler/utility/graph/optimization.py +27 -0
- scaler/utility/graph/topological_sorter.py +11 -0
- scaler/utility/graph/topological_sorter_graphblas.py +174 -0
- scaler/utility/identifiers.py +107 -0
- scaler/utility/logging/__init__.py +0 -0
- scaler/utility/logging/decorators.py +25 -0
- scaler/utility/logging/scoped_logger.py +33 -0
- scaler/utility/logging/utility.py +183 -0
- scaler/utility/many_to_many_dict.py +123 -0
- scaler/utility/metadata/__init__.py +0 -0
- scaler/utility/metadata/profile_result.py +31 -0
- scaler/utility/metadata/task_flags.py +30 -0
- scaler/utility/mixins.py +13 -0
- scaler/utility/network_util.py +7 -0
- scaler/utility/one_to_many_dict.py +72 -0
- scaler/utility/queues/__init__.py +0 -0
- scaler/utility/queues/async_indexed_queue.py +37 -0
- scaler/utility/queues/async_priority_queue.py +70 -0
- scaler/utility/queues/async_sorted_priority_queue.py +45 -0
- scaler/utility/queues/indexed_queue.py +114 -0
- scaler/utility/serialization.py +9 -0
- scaler/version.txt +1 -0
- scaler/worker/__init__.py +0 -0
- scaler/worker/agent/__init__.py +0 -0
- scaler/worker/agent/heartbeat_manager.py +110 -0
- scaler/worker/agent/mixins.py +137 -0
- scaler/worker/agent/processor/__init__.py +0 -0
- scaler/worker/agent/processor/object_cache.py +107 -0
- scaler/worker/agent/processor/processor.py +285 -0
- scaler/worker/agent/processor/streaming_buffer.py +28 -0
- scaler/worker/agent/processor_holder.py +147 -0
- scaler/worker/agent/processor_manager.py +369 -0
- scaler/worker/agent/profiling_manager.py +109 -0
- scaler/worker/agent/task_manager.py +150 -0
- scaler/worker/agent/timeout_manager.py +19 -0
- scaler/worker/preload.py +84 -0
- scaler/worker/worker.py +265 -0
- scaler/worker_adapter/__init__.py +0 -0
- scaler/worker_adapter/common.py +26 -0
- scaler/worker_adapter/ecs.py +241 -0
- scaler/worker_adapter/native.py +138 -0
- scaler/worker_adapter/symphony/__init__.py +0 -0
- scaler/worker_adapter/symphony/callback.py +45 -0
- scaler/worker_adapter/symphony/heartbeat_manager.py +82 -0
- scaler/worker_adapter/symphony/message.py +24 -0
- scaler/worker_adapter/symphony/task_manager.py +289 -0
- scaler/worker_adapter/symphony/worker.py +204 -0
- scaler/worker_adapter/symphony/worker_adapter.py +123 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import curses
|
|
2
|
+
import functools
|
|
3
|
+
from typing import Dict, List, Literal, Union
|
|
4
|
+
|
|
5
|
+
from scaler.config.section.top import TopConfig
|
|
6
|
+
from scaler.io.sync_subscriber import ZMQSyncSubscriber
|
|
7
|
+
from scaler.protocol.python.message import StateScheduler
|
|
8
|
+
from scaler.protocol.python.mixins import Message
|
|
9
|
+
from scaler.utility.formatter import (
|
|
10
|
+
format_bytes,
|
|
11
|
+
format_integer,
|
|
12
|
+
format_microseconds,
|
|
13
|
+
format_percentage,
|
|
14
|
+
format_seconds,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
SORT_BY_OPTIONS = {
|
|
18
|
+
ord("g"): "group",
|
|
19
|
+
ord("n"): "worker",
|
|
20
|
+
ord("C"): "agt_cpu",
|
|
21
|
+
ord("M"): "agt_rss",
|
|
22
|
+
ord("c"): "cpu",
|
|
23
|
+
ord("m"): "rss",
|
|
24
|
+
ord("F"): "rss_free",
|
|
25
|
+
ord("f"): "free",
|
|
26
|
+
ord("w"): "sent",
|
|
27
|
+
ord("d"): "queued",
|
|
28
|
+
ord("s"): "suspended",
|
|
29
|
+
ord("l"): "lag",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
SORT_BY_STATE: Dict[str, Union[str, bool]] = {"sort_by": "cpu", "sort_by_previous": "cpu", "sort_reverse": True}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def main():
|
|
36
|
+
curses.wrapper(poke, TopConfig.parse("A Top-like Application for Scaler Monitoring", "top"))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def poke(screen, config: TopConfig):
|
|
40
|
+
screen.nodelay(1)
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
subscriber = ZMQSyncSubscriber(
|
|
44
|
+
address=config.monitor_address,
|
|
45
|
+
callback=functools.partial(show_status, screen=screen),
|
|
46
|
+
topic=b"",
|
|
47
|
+
daemonic=False,
|
|
48
|
+
timeout_seconds=config.timeout,
|
|
49
|
+
)
|
|
50
|
+
subscriber.run()
|
|
51
|
+
except KeyboardInterrupt:
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def show_status(status: Message, screen):
|
|
56
|
+
if not isinstance(status, StateScheduler):
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
__change_option_state(screen.getch())
|
|
60
|
+
|
|
61
|
+
scheduler_table = __generate_keyword_data(
|
|
62
|
+
"scheduler",
|
|
63
|
+
{
|
|
64
|
+
"cpu": format_percentage(status.scheduler.cpu),
|
|
65
|
+
"rss": format_bytes(status.scheduler.rss),
|
|
66
|
+
"rss_free": format_bytes(status.rss_free),
|
|
67
|
+
},
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
task_manager_table = __generate_keyword_data(
|
|
71
|
+
"task_manager",
|
|
72
|
+
dict(sorted((k.name, v) for k, v in status.task_manager.state_to_count.items())),
|
|
73
|
+
format_integer_flag=True,
|
|
74
|
+
)
|
|
75
|
+
object_manager = __generate_keyword_data("object_manager", {"num_of_objs": status.object_manager.number_of_objects})
|
|
76
|
+
sent_table = __generate_keyword_data("scheduler_sent", status.binder.sent, format_integer_flag=True)
|
|
77
|
+
received_table = __generate_keyword_data("scheduler_received", status.binder.received, format_integer_flag=True)
|
|
78
|
+
client_table = __generate_keyword_data(
|
|
79
|
+
"client_manager", status.client_manager.client_to_num_of_tasks, key_col_length=18
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
worker_group_map = {}
|
|
83
|
+
if status.scaling_manager.worker_groups:
|
|
84
|
+
for worker_group_id, worker_ids in status.scaling_manager.worker_groups.items():
|
|
85
|
+
worker_group_id_str = worker_group_id.decode()
|
|
86
|
+
for worker_id in worker_ids:
|
|
87
|
+
worker_group_map[worker_id.decode()] = worker_group_id_str
|
|
88
|
+
|
|
89
|
+
# Include 'group' as the first column for each worker; empty if not found
|
|
90
|
+
worker_manager_table = __generate_worker_manager_table(
|
|
91
|
+
[
|
|
92
|
+
{
|
|
93
|
+
"group": worker_group_map.get(worker.worker_id.decode(), ""),
|
|
94
|
+
"worker": worker.worker_id.decode(),
|
|
95
|
+
"agt_cpu": worker.agent.cpu,
|
|
96
|
+
"agt_rss": worker.agent.rss,
|
|
97
|
+
"cpu": sum(p.resource.cpu for p in worker.processor_statuses),
|
|
98
|
+
"rss": sum(p.resource.rss for p in worker.processor_statuses),
|
|
99
|
+
"os_rss_free": worker.rss_free,
|
|
100
|
+
"free": worker.free,
|
|
101
|
+
"sent": worker.sent,
|
|
102
|
+
"queued": worker.queued,
|
|
103
|
+
"suspended": worker.suspended,
|
|
104
|
+
"lag": worker.lag_us,
|
|
105
|
+
"last": worker.last_s,
|
|
106
|
+
"ITL": worker.itl,
|
|
107
|
+
}
|
|
108
|
+
for worker in status.worker_manager.workers
|
|
109
|
+
],
|
|
110
|
+
worker_group_length=10,
|
|
111
|
+
worker_length=20,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
table1 = __merge_tables(scheduler_table, object_manager, padding="|")
|
|
115
|
+
table1 = __merge_tables(table1, task_manager_table, padding="|")
|
|
116
|
+
table1 = __merge_tables(table1, sent_table, padding="|")
|
|
117
|
+
table1 = __merge_tables(table1, received_table, padding="|")
|
|
118
|
+
|
|
119
|
+
table3 = __merge_tables(worker_manager_table, client_table, padding="|")
|
|
120
|
+
|
|
121
|
+
screen.clear()
|
|
122
|
+
try:
|
|
123
|
+
new_row, max_cols = __print_table(screen, 0, table1, padding=1)
|
|
124
|
+
except curses.error:
|
|
125
|
+
__print_too_small(screen)
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
screen.addstr(new_row, 0, "-" * max_cols)
|
|
130
|
+
screen.addstr(new_row + 1, 0, "Shortcuts: " + " ".join([f"{v}[{chr(k)}]" for k, v in SORT_BY_OPTIONS.items()]))
|
|
131
|
+
screen.addstr(
|
|
132
|
+
new_row + 3,
|
|
133
|
+
0,
|
|
134
|
+
f"Total {len(status.scaling_manager.worker_groups)} worker group(s) "
|
|
135
|
+
f"with {len(status.worker_manager.workers)} worker(s)",
|
|
136
|
+
)
|
|
137
|
+
_ = __print_table(screen, new_row + 4, table3)
|
|
138
|
+
except curses.error:
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
screen.refresh()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def __generate_keyword_data(title, data, key_col_length: int = 0, format_integer_flag: bool = False):
|
|
145
|
+
table = [[title, ""]]
|
|
146
|
+
|
|
147
|
+
def format_integer_func(value):
|
|
148
|
+
if format_integer_flag:
|
|
149
|
+
return format_integer(value)
|
|
150
|
+
|
|
151
|
+
return value
|
|
152
|
+
|
|
153
|
+
table.extend([[__truncate(k, key_col_length), format_integer_func(v)] for k, v in data.items()])
|
|
154
|
+
return table
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def __generate_worker_manager_table(
|
|
158
|
+
wm_data: List[Dict], worker_group_length: int, worker_length: int
|
|
159
|
+
) -> List[List[str]]:
|
|
160
|
+
if not wm_data:
|
|
161
|
+
headers = [["No workers"]]
|
|
162
|
+
return headers
|
|
163
|
+
|
|
164
|
+
wm_data = sorted(
|
|
165
|
+
wm_data, key=lambda item: item[SORT_BY_STATE["sort_by"]], reverse=bool(SORT_BY_STATE["sort_reverse"])
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
for row in wm_data:
|
|
169
|
+
row["group"] = __truncate(row["group"], worker_group_length, how="left")
|
|
170
|
+
row["worker"] = __truncate(row["worker"], worker_length, how="left")
|
|
171
|
+
row["agt_cpu"] = format_percentage(row["agt_cpu"])
|
|
172
|
+
row["agt_rss"] = format_bytes(row["agt_rss"])
|
|
173
|
+
row["cpu"] = format_percentage(row["cpu"])
|
|
174
|
+
row["rss"] = format_bytes(row["rss"])
|
|
175
|
+
row["os_rss_free"] = format_bytes(row["os_rss_free"])
|
|
176
|
+
|
|
177
|
+
last = row.pop("last")
|
|
178
|
+
last = f"({format_seconds(last)}) " if last > 5 else ""
|
|
179
|
+
row["lag"] = last + format_microseconds(row["lag"])
|
|
180
|
+
|
|
181
|
+
worker_manager_table = [[f"[{v}]" if v == SORT_BY_STATE["sort_by"] else v for v in wm_data[0].keys()]]
|
|
182
|
+
worker_manager_table.extend([list(worker.values()) for worker in wm_data])
|
|
183
|
+
return worker_manager_table
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def __print_table(screen, line_number, data, padding: int = 1):
|
|
187
|
+
if not data:
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
col_widths = [max(len(str(row[i])) for row in data) for i in range(len(data[0]))]
|
|
191
|
+
|
|
192
|
+
for i, header in enumerate(data[0]):
|
|
193
|
+
screen.addstr(line_number, sum(col_widths[:i]) + (padding * i), str(header).rjust(col_widths[i]))
|
|
194
|
+
|
|
195
|
+
for i, row in enumerate(data[1:], start=1):
|
|
196
|
+
for j, cell in enumerate(row):
|
|
197
|
+
screen.addstr(line_number + i, sum(col_widths[:j]) + (padding * j), str(cell).rjust(col_widths[j]))
|
|
198
|
+
|
|
199
|
+
return line_number + len(data), sum(col_widths) + (padding * len(col_widths))
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def __merge_tables(left: List[List], right: List[List], padding: str = "") -> List[List]:
|
|
203
|
+
if not left:
|
|
204
|
+
return right
|
|
205
|
+
|
|
206
|
+
if not right:
|
|
207
|
+
return left
|
|
208
|
+
|
|
209
|
+
result = []
|
|
210
|
+
for i in range(max(len(left), len(right))):
|
|
211
|
+
if i < len(left):
|
|
212
|
+
left_row = left[i]
|
|
213
|
+
else:
|
|
214
|
+
left_row = [""] * len(left[0])
|
|
215
|
+
|
|
216
|
+
if i < len(right):
|
|
217
|
+
right_row = right[i]
|
|
218
|
+
else:
|
|
219
|
+
right_row = [""] * len(right[0])
|
|
220
|
+
|
|
221
|
+
if padding:
|
|
222
|
+
padding_column = [padding]
|
|
223
|
+
result.append(left_row + padding_column + right_row)
|
|
224
|
+
else:
|
|
225
|
+
result.append(left_row + right_row)
|
|
226
|
+
|
|
227
|
+
return result
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def __concat_tables(up: List[List], down: List[List], padding: int = 1) -> List[List]:
|
|
231
|
+
max_cols = max([len(row) for row in up] + [len(row) for row in down])
|
|
232
|
+
for row in up:
|
|
233
|
+
row.extend([""] * (max_cols - len(row)))
|
|
234
|
+
|
|
235
|
+
padding_rows = [[""] * max_cols] * padding
|
|
236
|
+
|
|
237
|
+
for row in down:
|
|
238
|
+
row.extend([""] * (max_cols - len(row)))
|
|
239
|
+
|
|
240
|
+
return up + padding_rows + down
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def __truncate(string: str, number: int, how: Literal["left", "right"] = "left") -> str:
|
|
244
|
+
if number <= 0:
|
|
245
|
+
return string
|
|
246
|
+
|
|
247
|
+
if len(string) <= number:
|
|
248
|
+
return string
|
|
249
|
+
|
|
250
|
+
if how == "left":
|
|
251
|
+
return f"{string[:number]}+"
|
|
252
|
+
else:
|
|
253
|
+
return f"+{string[-number:]}"
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def __print_too_small(screen):
|
|
257
|
+
screen.clear()
|
|
258
|
+
screen.addstr(0, 0, "Your terminal is too small to show")
|
|
259
|
+
screen.refresh()
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def __change_option_state(option: int):
|
|
263
|
+
if option not in SORT_BY_OPTIONS.keys():
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
SORT_BY_STATE["sort_by_previous"] = SORT_BY_STATE["sort_by"]
|
|
267
|
+
SORT_BY_STATE["sort_by"] = SORT_BY_OPTIONS[option]
|
|
268
|
+
if SORT_BY_STATE["sort_by"] != SORT_BY_STATE["sort_by_previous"]:
|
|
269
|
+
SORT_BY_STATE["sort_reverse"] = True
|
|
270
|
+
return
|
|
271
|
+
|
|
272
|
+
SORT_BY_STATE["sort_reverse"] = not SORT_BY_STATE["sort_reverse"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from aiohttp import web
|
|
2
|
+
|
|
3
|
+
from scaler.config.section.ecs_worker_adapter import ECSWorkerAdapterConfig
|
|
4
|
+
from scaler.utility.event_loop import register_event_loop
|
|
5
|
+
from scaler.utility.logging.utility import setup_logger
|
|
6
|
+
from scaler.worker_adapter.ecs import ECSWorkerAdapter
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
ecs_config = ECSWorkerAdapterConfig.parse("Scaler ECS Worker Adapter", "ecs_worker_adapter")
|
|
11
|
+
register_event_loop(ecs_config.event_loop)
|
|
12
|
+
setup_logger(
|
|
13
|
+
ecs_config.logging_config.paths, ecs_config.logging_config.config_file, ecs_config.logging_config.level
|
|
14
|
+
)
|
|
15
|
+
ecs_worker_adapter = ECSWorkerAdapter(ecs_config)
|
|
16
|
+
|
|
17
|
+
app = ecs_worker_adapter.create_app()
|
|
18
|
+
web.run_app(app, host=ecs_config.web_config.adapter_web_host, port=ecs_config.web_config.adapter_web_port)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
if __name__ == "__main__":
|
|
22
|
+
main()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from aiohttp import web
|
|
2
|
+
|
|
3
|
+
from scaler.config.section.native_worker_adapter import NativeWorkerAdapterConfig
|
|
4
|
+
from scaler.utility.event_loop import register_event_loop
|
|
5
|
+
from scaler.utility.logging.utility import setup_logger
|
|
6
|
+
from scaler.worker_adapter.native import NativeWorkerAdapter
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
native_adapter_config = NativeWorkerAdapterConfig.parse("Scaler Native Worker Adapter", "native_worker_adapter")
|
|
11
|
+
|
|
12
|
+
register_event_loop(native_adapter_config.event_loop)
|
|
13
|
+
|
|
14
|
+
setup_logger(
|
|
15
|
+
native_adapter_config.logging_config.paths,
|
|
16
|
+
native_adapter_config.logging_config.config_file,
|
|
17
|
+
native_adapter_config.logging_config.level,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
native_worker_adapter = NativeWorkerAdapter(native_adapter_config)
|
|
21
|
+
|
|
22
|
+
app = native_worker_adapter.create_app()
|
|
23
|
+
web.run_app(
|
|
24
|
+
app,
|
|
25
|
+
host=native_adapter_config.web_config.adapter_web_host,
|
|
26
|
+
port=native_adapter_config.web_config.adapter_web_port,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
main()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from aiohttp import web
|
|
2
|
+
|
|
3
|
+
from scaler.config.section.symphony_worker_adapter import SymphonyWorkerConfig
|
|
4
|
+
from scaler.utility.event_loop import register_event_loop
|
|
5
|
+
from scaler.utility.logging.utility import setup_logger
|
|
6
|
+
from scaler.worker_adapter.symphony.worker_adapter import SymphonyWorkerAdapter
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
symphony_config = SymphonyWorkerConfig.parse("Scaler Symphony Worker Adapter", "symphony_worker_adapter")
|
|
11
|
+
register_event_loop(symphony_config.event_loop)
|
|
12
|
+
|
|
13
|
+
setup_logger(
|
|
14
|
+
symphony_config.logging_config.paths,
|
|
15
|
+
symphony_config.logging_config.config_file,
|
|
16
|
+
symphony_config.logging_config.level,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
symphony_worker_adapter = SymphonyWorkerAdapter(symphony_config)
|
|
20
|
+
|
|
21
|
+
app = symphony_worker_adapter.create_app()
|
|
22
|
+
web.run_app(app, host=symphony_config.web_config.adapter_web_host, port=symphony_config.web_config.adapter_web_port)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
main()
|
scaler/io/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import uuid
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
from typing import Awaitable, Callable, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
import zmq.asyncio
|
|
8
|
+
from zmq import Frame
|
|
9
|
+
|
|
10
|
+
from scaler.config.types.zmq import ZMQConfig
|
|
11
|
+
from scaler.io.mixins import AsyncBinder
|
|
12
|
+
from scaler.io.utility import deserialize, serialize
|
|
13
|
+
from scaler.protocol.python.mixins import Message
|
|
14
|
+
from scaler.protocol.python.status import BinderStatus
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ZMQAsyncBinder(AsyncBinder):
|
|
18
|
+
def __init__(self, context: zmq.asyncio.Context, name: str, address: ZMQConfig, identity: Optional[bytes] = None):
|
|
19
|
+
self._address = address
|
|
20
|
+
|
|
21
|
+
if identity is None:
|
|
22
|
+
identity = f"{os.getpid()}|{name}|{uuid.uuid4()}".encode()
|
|
23
|
+
self._identity = identity
|
|
24
|
+
|
|
25
|
+
self._context = context
|
|
26
|
+
self._socket = self._context.socket(zmq.ROUTER)
|
|
27
|
+
self.__set_socket_options()
|
|
28
|
+
self._socket.bind(self._address.to_address())
|
|
29
|
+
|
|
30
|
+
self._callback: Optional[Callable[[bytes, Message], Awaitable[None]]] = None
|
|
31
|
+
|
|
32
|
+
self._received: Dict[str, int] = defaultdict(lambda: 0)
|
|
33
|
+
self._sent: Dict[str, int] = defaultdict(lambda: 0)
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def identity(self):
|
|
37
|
+
return self._identity
|
|
38
|
+
|
|
39
|
+
def destroy(self):
|
|
40
|
+
self._context.destroy(linger=0)
|
|
41
|
+
|
|
42
|
+
def register(self, callback: Callable[[bytes, Message], Awaitable[None]]):
|
|
43
|
+
self._callback = callback
|
|
44
|
+
|
|
45
|
+
async def routine(self):
|
|
46
|
+
frames: List[Frame] = await self._socket.recv_multipart(copy=False)
|
|
47
|
+
if not self.__is_valid_message(frames):
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
source, payload = frames
|
|
51
|
+
try:
|
|
52
|
+
message: Optional[Message] = deserialize(payload.bytes)
|
|
53
|
+
if message is None:
|
|
54
|
+
logging.error(f"received unknown message from {source.bytes!r}: {payload!r}")
|
|
55
|
+
return
|
|
56
|
+
except Exception as e:
|
|
57
|
+
logging.error(f"{self.__get_prefix()} failed to deserialize message from {source.bytes!r}: {e}")
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
self.__count_received(message.__class__.__name__)
|
|
61
|
+
await self._callback(source.bytes, message)
|
|
62
|
+
|
|
63
|
+
async def send(self, to: bytes, message: Message):
|
|
64
|
+
self.__count_sent(message.__class__.__name__)
|
|
65
|
+
await self._socket.send_multipart([to, serialize(message)], copy=False)
|
|
66
|
+
|
|
67
|
+
def get_status(self) -> BinderStatus:
|
|
68
|
+
return BinderStatus.new_msg(received=self._received, sent=self._sent)
|
|
69
|
+
|
|
70
|
+
def __set_socket_options(self):
|
|
71
|
+
self._socket.setsockopt(zmq.IDENTITY, self._identity)
|
|
72
|
+
self._socket.setsockopt(zmq.SNDHWM, 0)
|
|
73
|
+
self._socket.setsockopt(zmq.RCVHWM, 0)
|
|
74
|
+
|
|
75
|
+
def __is_valid_message(self, frames: List[Frame]) -> bool:
|
|
76
|
+
if len(frames) != 2:
|
|
77
|
+
logging.error(f"{self.__get_prefix()} received unexpected frames {frames}")
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
def __count_received(self, message_type: str):
|
|
83
|
+
self._received[message_type] += 1
|
|
84
|
+
|
|
85
|
+
def __count_sent(self, message_type: str):
|
|
86
|
+
self._sent[message_type] += 1
|
|
87
|
+
|
|
88
|
+
def __get_prefix(self):
|
|
89
|
+
return f"{self.__class__.__name__}[{self._identity.decode()}]:"
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import uuid
|
|
4
|
+
from typing import Awaitable, Callable, Literal, Optional
|
|
5
|
+
|
|
6
|
+
import zmq.asyncio
|
|
7
|
+
|
|
8
|
+
from scaler.config.types.zmq import ZMQConfig
|
|
9
|
+
from scaler.io.mixins import AsyncConnector
|
|
10
|
+
from scaler.io.utility import deserialize, serialize
|
|
11
|
+
from scaler.protocol.python.mixins import Message
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ZMQAsyncConnector(AsyncConnector):
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
context: zmq.asyncio.Context,
|
|
18
|
+
name: str,
|
|
19
|
+
socket_type: int,
|
|
20
|
+
address: ZMQConfig,
|
|
21
|
+
bind_or_connect: Literal["bind", "connect"],
|
|
22
|
+
callback: Optional[Callable[[Message], Awaitable[None]]],
|
|
23
|
+
identity: Optional[bytes],
|
|
24
|
+
):
|
|
25
|
+
self._address = address
|
|
26
|
+
|
|
27
|
+
self._context = context
|
|
28
|
+
self._socket = self._context.socket(socket_type)
|
|
29
|
+
|
|
30
|
+
if identity is None:
|
|
31
|
+
identity = f"{os.getpid()}|{name}|{uuid.uuid4().bytes.hex()}".encode()
|
|
32
|
+
self._identity = identity
|
|
33
|
+
|
|
34
|
+
# set socket option
|
|
35
|
+
self._socket.setsockopt(zmq.IDENTITY, self._identity)
|
|
36
|
+
self._socket.setsockopt(zmq.SNDHWM, 0)
|
|
37
|
+
self._socket.setsockopt(zmq.RCVHWM, 0)
|
|
38
|
+
|
|
39
|
+
if bind_or_connect == "bind":
|
|
40
|
+
self._socket.bind(self.address)
|
|
41
|
+
elif bind_or_connect == "connect":
|
|
42
|
+
self._socket.connect(self.address)
|
|
43
|
+
else:
|
|
44
|
+
raise TypeError("bind_or_connect has to be 'bind' or 'connect'")
|
|
45
|
+
|
|
46
|
+
self._callback: Optional[Callable[[Message], Awaitable[None]]] = callback
|
|
47
|
+
|
|
48
|
+
def __del__(self):
|
|
49
|
+
self.destroy()
|
|
50
|
+
|
|
51
|
+
def destroy(self):
|
|
52
|
+
if self._socket.closed:
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
self._socket.close(linger=1)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def identity(self) -> bytes:
|
|
59
|
+
return self._identity
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def socket(self) -> zmq.asyncio.Socket:
|
|
63
|
+
return self._socket
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def address(self) -> str:
|
|
67
|
+
return self._address.to_address()
|
|
68
|
+
|
|
69
|
+
async def routine(self):
|
|
70
|
+
if self._callback is None:
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
message: Optional[Message] = await self.receive()
|
|
74
|
+
if message is None:
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
await self._callback(message)
|
|
78
|
+
|
|
79
|
+
async def receive(self) -> Optional[Message]:
|
|
80
|
+
if self._context.closed:
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
if self._socket.closed:
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
payload = await self._socket.recv(copy=False)
|
|
87
|
+
result: Optional[Message] = deserialize(payload.bytes)
|
|
88
|
+
if result is None:
|
|
89
|
+
logging.error(f"received unknown message: {payload.bytes!r}")
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
return result
|
|
93
|
+
|
|
94
|
+
async def send(self, message: Message):
|
|
95
|
+
await self._socket.send(serialize(message), copy=False)
|