ds-service-client 0.2.0__tar.gz

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 (33) hide show
  1. ds_service_client-0.2.0/.clang-format +10 -0
  2. ds_service_client-0.2.0/.cpush.json5 +6 -0
  3. ds_service_client-0.2.0/.gitignore +284 -0
  4. ds_service_client-0.2.0/CMakeLists.txt +58 -0
  5. ds_service_client-0.2.0/LICENSE +19 -0
  6. ds_service_client-0.2.0/PKG-INFO +144 -0
  7. ds_service_client-0.2.0/README.md +128 -0
  8. ds_service_client-0.2.0/conanfile.py +49 -0
  9. ds_service_client-0.2.0/cpp/ds-server.cpp +272 -0
  10. ds_service_client-0.2.0/cpp/task_table.hpp +273 -0
  11. ds_service_client-0.2.0/misc/ds-service.proto +72 -0
  12. ds_service_client-0.2.0/misc/task_table.json5 +20 -0
  13. ds_service_client-0.2.0/pyproject.toml +32 -0
  14. ds_service_client-0.2.0/python/ds_service_client/__init__.py +1 -0
  15. ds_service_client-0.2.0/python/ds_service_client/client.py +94 -0
  16. ds_service_client-0.2.0/python/ds_service_client/ds-service.proto +72 -0
  17. ds_service_client-0.2.0/python/ds_service_client/ds_service_pb2.py +60 -0
  18. ds_service_client-0.2.0/python/ds_service_client/ds_service_pb2.pyi +101 -0
  19. ds_service_client-0.2.0/python/ds_service_client/ds_service_pb2_grpc.py +355 -0
  20. ds_service_client-0.2.0/python/ds_service_client.egg-info/PKG-INFO +144 -0
  21. ds_service_client-0.2.0/python/ds_service_client.egg-info/SOURCES.txt +31 -0
  22. ds_service_client-0.2.0/python/ds_service_client.egg-info/dependency_links.txt +1 -0
  23. ds_service_client-0.2.0/python/ds_service_client.egg-info/requires.txt +2 -0
  24. ds_service_client-0.2.0/python/ds_service_client.egg-info/scm_file_list.json +28 -0
  25. ds_service_client-0.2.0/python/ds_service_client.egg-info/scm_version.json +8 -0
  26. ds_service_client-0.2.0/python/ds_service_client.egg-info/top_level.txt +1 -0
  27. ds_service_client-0.2.0/scripts/apptainer/ds-server.def +57 -0
  28. ds_service_client-0.2.0/scripts/apptainer/profiles-default +28 -0
  29. ds_service_client-0.2.0/scripts/apptainer/remotes.json +14 -0
  30. ds_service_client-0.2.0/scripts/gen_cppsoa.sh +6 -0
  31. ds_service_client-0.2.0/scripts/gen_python_bindings.sh +17 -0
  32. ds_service_client-0.2.0/scripts/pb-dev.sh +80 -0
  33. ds_service_client-0.2.0/setup.cfg +4 -0
@@ -0,0 +1,10 @@
1
+ BasedOnStyle: LLVM
2
+ IndentWidth: 4
3
+ ColumnLimit: 120
4
+ DerivePointerAlignment: false
5
+ PointerAlignment: Left
6
+ BreakConstructorInitializers: BeforeComma
7
+ SortIncludes: false
8
+ AllowShortFunctionsOnASingleLine: Empty
9
+ BreakTemplateDeclarations: Yes
10
+ AlignEscapedNewlines: Left
@@ -0,0 +1,6 @@
1
+ {
2
+ project: "ds-service",
3
+ remotes: {
4
+ rivanna: "rivanna:ds-service"
5
+ }
6
+ }
@@ -0,0 +1,284 @@
1
+ # Prerequisites
2
+ *.d
3
+
4
+ # Compiled Object files
5
+ *.slo
6
+ *.lo
7
+ *.o
8
+ *.obj
9
+
10
+ # Precompiled Headers
11
+ *.gch
12
+ *.pch
13
+
14
+ # Linker files
15
+ *.ilk
16
+
17
+ # Debugger Files
18
+ *.pdb
19
+
20
+ # Compiled Dynamic libraries
21
+ *.so
22
+ *.dylib
23
+ *.dll
24
+ *.so.*
25
+
26
+
27
+ # Fortran module files
28
+ *.mod
29
+ *.smod
30
+
31
+ # Compiled Static libraries
32
+ *.lai
33
+ *.la
34
+ *.a
35
+ *.lib
36
+
37
+ # Executables
38
+ *.exe
39
+ *.out
40
+ *.app
41
+
42
+ # Build directories
43
+ build/
44
+ Build/
45
+ build-*/
46
+
47
+ # CMake generated files
48
+ CMakeFiles/
49
+ CMakeCache.txt
50
+ cmake_install.cmake
51
+ Makefile
52
+ install_manifest.txt
53
+ compile_commands.json
54
+
55
+ # Temporary files
56
+ *.tmp
57
+ *.log
58
+ *.bak
59
+ *.swp
60
+
61
+ # vcpkg
62
+ vcpkg_installed/
63
+
64
+ # debug information files
65
+ *.dwo
66
+
67
+ # test output & cache
68
+ Testing/
69
+ .cache/# Byte-compiled / optimized / DLL files
70
+ __pycache__/
71
+ *.py[codz]
72
+ *$py.class
73
+
74
+ # C extensions
75
+ *.so
76
+
77
+ # Distribution / packaging
78
+ .Python
79
+ build/
80
+ develop-eggs/
81
+ dist/
82
+ downloads/
83
+ eggs/
84
+ .eggs/
85
+ lib/
86
+ lib64/
87
+ parts/
88
+ sdist/
89
+ var/
90
+ wheels/
91
+ share/python-wheels/
92
+ *.egg-info/
93
+ .installed.cfg
94
+ *.egg
95
+ MANIFEST
96
+
97
+ # PyInstaller
98
+ # Usually these files are written by a python script from a template
99
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
100
+ *.manifest
101
+ *.spec
102
+
103
+ # Installer logs
104
+ pip-log.txt
105
+ pip-delete-this-directory.txt
106
+
107
+ # Unit test / coverage reports
108
+ htmlcov/
109
+ .tox/
110
+ .nox/
111
+ .coverage
112
+ .coverage.*
113
+ .cache
114
+ nosetests.xml
115
+ coverage.xml
116
+ *.cover
117
+ *.py.cover
118
+ .hypothesis/
119
+ .pytest_cache/
120
+ cover/
121
+
122
+ # Translations
123
+ *.mo
124
+ *.pot
125
+
126
+ # Django stuff:
127
+ *.log
128
+ local_settings.py
129
+ db.sqlite3
130
+ db.sqlite3-journal
131
+
132
+ # Flask stuff:
133
+ instance/
134
+ .webassets-cache
135
+
136
+ # Scrapy stuff:
137
+ .scrapy
138
+
139
+ # Sphinx documentation
140
+ docs/_build/
141
+
142
+ # PyBuilder
143
+ .pybuilder/
144
+ target/
145
+
146
+ # Jupyter Notebook
147
+ .ipynb_checkpoints
148
+
149
+ # IPython
150
+ profile_default/
151
+ ipython_config.py
152
+
153
+ # pyenv
154
+ # For a library or package, you might want to ignore these files since the code is
155
+ # intended to run in multiple environments; otherwise, check them in:
156
+ # .python-version
157
+
158
+ # pipenv
159
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
160
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
161
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
162
+ # install all needed dependencies.
163
+ # Pipfile.lock
164
+
165
+ # UV
166
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
167
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
168
+ # commonly ignored for libraries.
169
+ # uv.lock
170
+
171
+ # poetry
172
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
173
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
174
+ # commonly ignored for libraries.
175
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
176
+ # poetry.lock
177
+ # poetry.toml
178
+
179
+ # pdm
180
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
181
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
182
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
183
+ # pdm.lock
184
+ # pdm.toml
185
+ .pdm-python
186
+ .pdm-build/
187
+
188
+ # pixi
189
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
190
+ # pixi.lock
191
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
192
+ # in the .venv directory. It is recommended not to include this directory in version control.
193
+ .pixi
194
+
195
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
196
+ __pypackages__/
197
+
198
+ # Celery stuff
199
+ celerybeat-schedule
200
+ celerybeat.pid
201
+
202
+ # Redis
203
+ *.rdb
204
+ *.aof
205
+ *.pid
206
+
207
+ # RabbitMQ
208
+ mnesia/
209
+ rabbitmq/
210
+ rabbitmq-data/
211
+
212
+ # ActiveMQ
213
+ activemq-data/
214
+
215
+ # SageMath parsed files
216
+ *.sage.py
217
+
218
+ # Environments
219
+ .env
220
+ .envrc
221
+ .venv
222
+ env/
223
+ venv/
224
+ ENV/
225
+ env.bak/
226
+ venv.bak/
227
+
228
+ # Spyder project settings
229
+ .spyderproject
230
+ .spyproject
231
+
232
+ # Rope project settings
233
+ .ropeproject
234
+
235
+ # mkdocs documentation
236
+ /site
237
+
238
+ # mypy
239
+ .mypy_cache/
240
+ .dmypy.json
241
+ dmypy.json
242
+
243
+ # Pyre type checker
244
+ .pyre/
245
+
246
+ # pytype static type analyzer
247
+ .pytype/
248
+
249
+ # Cython debug symbols
250
+ cython_debug/
251
+
252
+ # PyCharm
253
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
254
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
255
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
256
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
257
+ # .idea/
258
+
259
+ # Abstra
260
+ # Abstra is an AI-powered process automation framework.
261
+ # Ignore directories containing user credentials, local state, and settings.
262
+ # Learn more at https://abstra.io/docs
263
+ .abstra/
264
+
265
+ # Visual Studio Code
266
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
267
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
268
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
269
+ # you could uncomment the following to ignore the entire vscode folder
270
+ # .vscode/
271
+
272
+ # Ruff stuff:
273
+ .ruff_cache/
274
+
275
+ # PyPI configuration file
276
+ .pypirc
277
+
278
+ # Marimo
279
+ marimo/_static/
280
+ marimo/_lsp/
281
+ __marimo__/
282
+
283
+ # Streamlit
284
+ .streamlit/secrets.toml
@@ -0,0 +1,58 @@
1
+ cmake_minimum_required(VERSION 4.0)
2
+
3
+ project(
4
+ ds-service
5
+ VERSION 0.0.1
6
+ LANGUAGES CXX)
7
+
8
+ find_package(OpenMP REQUIRED)
9
+
10
+ find_package(spdlog REQUIRED)
11
+ find_package(argparse REQUIRED)
12
+ find_package(phmap REQUIRED)
13
+ find_package(gRPC REQUIRED)
14
+ find_package(protobuf REQUIRED)
15
+
16
+ find_program(PROTOC_EXE protoc REQUIRED)
17
+ find_program(GRPC_CPP_PLUGIN_EXE grpc_cpp_plugin REQUIRED)
18
+
19
+ add_custom_command(
20
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ds-service.pb.h
21
+ ${CMAKE_CURRENT_BINARY_DIR}/ds-service.pb.cc
22
+ ${CMAKE_CURRENT_BINARY_DIR}/ds-service.grpc.pb.h
23
+ ${CMAKE_CURRENT_BINARY_DIR}/ds-service.grpc.pb.cc
24
+ COMMAND
25
+ ${PROTOC_EXE} --cpp_out=${CMAKE_CURRENT_BINARY_DIR}
26
+ --grpc_out=${CMAKE_CURRENT_BINARY_DIR}
27
+ --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN_EXE}
28
+ --proto_path=${CMAKE_CURRENT_SOURCE_DIR}/misc
29
+ ${CMAKE_CURRENT_SOURCE_DIR}/misc/ds-service.proto
30
+ DEPENDS misc/ds-service.proto
31
+ VERBATIM)
32
+
33
+ add_library(ds-service-grpc ${CMAKE_CURRENT_BINARY_DIR}/ds-service.pb.cc
34
+ ${CMAKE_CURRENT_BINARY_DIR}/ds-service.grpc.pb.cc)
35
+ target_sources(
36
+ ds-service-grpc
37
+ PUBLIC FILE_SET
38
+ HEADERS
39
+ BASE_DIRS
40
+ ${CMAKE_CURRENT_BINARY_DIR}
41
+ FILES
42
+ ${CMAKE_CURRENT_BINARY_DIR}/ds-service.pb.h
43
+ ${CMAKE_CURRENT_BINARY_DIR}/ds-service.grpc.pb.h)
44
+ target_link_libraries(ds-service-grpc PUBLIC protobuf::protobuf grpc::grpc)
45
+ target_compile_features(ds-service-grpc PRIVATE cxx_std_17)
46
+
47
+ add_executable(ds-server cpp/ds-server.cpp)
48
+ target_sources(ds-server PRIVATE FILE_SET HEADERS BASE_DIRS cpp)
49
+ target_link_libraries(
50
+ ds-server
51
+ PRIVATE spdlog::spdlog
52
+ argparse::argparse
53
+ phmap
54
+ ds-service-grpc
55
+ OpenMP::OpenMP_CXX)
56
+ target_compile_features(ds-server PRIVATE cxx_std_23)
57
+
58
+ install(TARGETS ds-server)
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2026 Rector and Visitors of the University of Virginia
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,144 @@
1
+ Metadata-Version: 2.4
2
+ Name: ds-service-client
3
+ Version: 0.2.0
4
+ Summary: Python client for DS service.
5
+ Author-email: Parantapa Bhattacharya <pb@parantapa.net>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/parantapa/ds-service
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.12
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: grpcio>=1.60
14
+ Requires-Dist: protobuf>=6.31
15
+ Dynamic: license-file
16
+
17
+ # ds-service: Yet Another Data Structure Server
18
+
19
+ `ds-service` is a small, in-memory data structure server that is accessible via [gRPC](https://grpc.io/).
20
+
21
+ `ds-service` runs a single server process (`ds-server`)
22
+ that holds shared state in memory
23
+ and lets many distributed clients and workers coordinate using it.
24
+
25
+ Presently, it provides two things:
26
+ - **A key-value store** -- a shared `string -> bytes` store
27
+ for passing data between processes.
28
+ - **A task queue** -- a priority-based work queue
29
+ that distributes tasks to workers,
30
+ tracks their state,
31
+ and requeues tasks whose workers have died or stalled.
32
+
33
+ It is designed as a lightweight coordination system for distributed batch jobs
34
+ (for example, when running calibration and projection workflows across many nodes of an HPC cluster),
35
+ where you need some shared data structures
36
+ and a way to hand work out to a pool of workers.
37
+
38
+ ## Architecture
39
+
40
+ - **Server** (`cpp/ds-server.cpp`) -- a C++23 gRPC service.
41
+ All state lives in memory and is guarded by a single global lock,
42
+ so operations are serialized and consistent.
43
+ State is **not** persisted; that is, when the server stops all data is lost.
44
+ - **Client** (`python/ds_service_client/`) -- a Python 3.12+ client library
45
+ that wraps the generated gRPC stubs
46
+ and translates gRPC status codes into ordinary Python exceptions
47
+ (`KeyError`, `ValueError`, `TimeoutError`).
48
+ - **Interface** (`misc/ds-service.proto`) -- the protobuf/gRPC contract
49
+ shared by both sides.
50
+
51
+ By default the server listens on `127.0.0.1:5051`.
52
+
53
+ ## The key-value store
54
+
55
+ A flat `string -> bytes` key-value store.
56
+
57
+ | RPC | Description |
58
+ | --- | --- |
59
+ | `MapSet(key, value)` | Store `value` under `key`, overwriting any existing value. |
60
+ | `MapGet(key)` | Return the value for `key`, or `NOT_FOUND` if it is missing. |
61
+
62
+ Values are opaque bytes, so callers are free to store whatever serialization
63
+ they like (JSON, pickle, protobuf, raw binary).
64
+
65
+ ## The task queue
66
+
67
+ Tasks are units of work identified by a unique `task_id`.
68
+ Each task carries an opaque `function` and `input` payload,
69
+ a floating-point `priority`,
70
+ and one or more named queues it should be dispatched from.
71
+ A task moves through three states: `Ready` → `Running` → `Complete`.
72
+
73
+ | RPC | Description |
74
+ | --- | --- |
75
+ | `TaskAdd(task_id, queue, priority, function, input)` | Register a new task and enqueue it on each named queue. Returns `ALREADY_EXISTS` if the id is already known. |
76
+ | `TaskGet(worker_id, queue)` | Claim the highest-priority `Ready` task from the first non-empty queue, mark it `Running`, and return its payload. Returns `UNAVAILABLE` when no work is ready. |
77
+ | `TaskDone(task_id, output)` | Mark a `Running` task `Complete` and store its output. |
78
+ | `TaskStatus(task_id)` | Return a task's current state and, once complete, its output. |
79
+ | `Requeue(timeout_s)` | Reset any task that has been `Running` longer than `timeout_s` back to `Ready` and re-enqueue it. |
80
+
81
+ Within a queue, higher `priority` values are dispatched first.
82
+ A worker polls `TaskGet` across the queues it cares about, runs the work,
83
+ and reports back with `TaskDone`.
84
+ `Requeue` provides fault tolerance:
85
+ if a worker crashes without completing its task,
86
+ a periodic `Requeue` call makes that task available to another worker.
87
+
88
+ ## Building the server
89
+
90
+ Dependencies are managed with [Conan](https://conan.io/)
91
+ and the build is driven by CMake.
92
+
93
+ ```sh
94
+ conan install .
95
+ . build/Release/generators/conanbuild.sh
96
+ cmake -S . -B build/Release \
97
+ -DCMAKE_BUILD_TYPE=Release \
98
+ -DCMAKE_TOOLCHAIN_FILE=generators/conan_toolchain.cmake
99
+ cmake --build build/Release --parallel
100
+ ```
101
+
102
+ A reproducible container build is defined in
103
+ `scripts/apptainer/ds-server.def`.
104
+
105
+ ### Running
106
+
107
+ ```sh
108
+ ds-server --address 0.0.0.0:5051
109
+ ```
110
+
111
+ Run `ds-server --help` for the full argument list.
112
+
113
+ ## Python client
114
+
115
+ ```sh
116
+ pip install . # installs the ds-service-client package
117
+ ```
118
+
119
+ ```python
120
+ from ds_service_client import Client, TaskState
121
+
122
+ client = Client("127.0.0.1:5051") # or set DS_SERVER_ADDRESS and call Client()
123
+
124
+ # Key-value map
125
+ client.map_set("greeting", b"hello")
126
+ assert client.map_get("greeting") == b"hello"
127
+
128
+ # Task queue
129
+ client.task_add("job-1", queue="work", priority=1.0, function=b"...", input=b"...")
130
+
131
+ task = client.task_get(worker_id="worker-a", queue="work")
132
+ # ... do the work ...
133
+ client.task_done(task.task_id, output=b"result")
134
+
135
+ status = client.task_status("job-1")
136
+ assert status.state == TaskState.Complete
137
+ ```
138
+
139
+ If `Client()` is constructed without an address, it reads the server address
140
+ from the `DS_SERVER_ADDRESS` environment variable.
141
+
142
+ ## License
143
+
144
+ MIT -- see [LICENSE](LICENSE).
@@ -0,0 +1,128 @@
1
+ # ds-service: Yet Another Data Structure Server
2
+
3
+ `ds-service` is a small, in-memory data structure server that is accessible via [gRPC](https://grpc.io/).
4
+
5
+ `ds-service` runs a single server process (`ds-server`)
6
+ that holds shared state in memory
7
+ and lets many distributed clients and workers coordinate using it.
8
+
9
+ Presently, it provides two things:
10
+ - **A key-value store** -- a shared `string -> bytes` store
11
+ for passing data between processes.
12
+ - **A task queue** -- a priority-based work queue
13
+ that distributes tasks to workers,
14
+ tracks their state,
15
+ and requeues tasks whose workers have died or stalled.
16
+
17
+ It is designed as a lightweight coordination system for distributed batch jobs
18
+ (for example, when running calibration and projection workflows across many nodes of an HPC cluster),
19
+ where you need some shared data structures
20
+ and a way to hand work out to a pool of workers.
21
+
22
+ ## Architecture
23
+
24
+ - **Server** (`cpp/ds-server.cpp`) -- a C++23 gRPC service.
25
+ All state lives in memory and is guarded by a single global lock,
26
+ so operations are serialized and consistent.
27
+ State is **not** persisted; that is, when the server stops all data is lost.
28
+ - **Client** (`python/ds_service_client/`) -- a Python 3.12+ client library
29
+ that wraps the generated gRPC stubs
30
+ and translates gRPC status codes into ordinary Python exceptions
31
+ (`KeyError`, `ValueError`, `TimeoutError`).
32
+ - **Interface** (`misc/ds-service.proto`) -- the protobuf/gRPC contract
33
+ shared by both sides.
34
+
35
+ By default the server listens on `127.0.0.1:5051`.
36
+
37
+ ## The key-value store
38
+
39
+ A flat `string -> bytes` key-value store.
40
+
41
+ | RPC | Description |
42
+ | --- | --- |
43
+ | `MapSet(key, value)` | Store `value` under `key`, overwriting any existing value. |
44
+ | `MapGet(key)` | Return the value for `key`, or `NOT_FOUND` if it is missing. |
45
+
46
+ Values are opaque bytes, so callers are free to store whatever serialization
47
+ they like (JSON, pickle, protobuf, raw binary).
48
+
49
+ ## The task queue
50
+
51
+ Tasks are units of work identified by a unique `task_id`.
52
+ Each task carries an opaque `function` and `input` payload,
53
+ a floating-point `priority`,
54
+ and one or more named queues it should be dispatched from.
55
+ A task moves through three states: `Ready` → `Running` → `Complete`.
56
+
57
+ | RPC | Description |
58
+ | --- | --- |
59
+ | `TaskAdd(task_id, queue, priority, function, input)` | Register a new task and enqueue it on each named queue. Returns `ALREADY_EXISTS` if the id is already known. |
60
+ | `TaskGet(worker_id, queue)` | Claim the highest-priority `Ready` task from the first non-empty queue, mark it `Running`, and return its payload. Returns `UNAVAILABLE` when no work is ready. |
61
+ | `TaskDone(task_id, output)` | Mark a `Running` task `Complete` and store its output. |
62
+ | `TaskStatus(task_id)` | Return a task's current state and, once complete, its output. |
63
+ | `Requeue(timeout_s)` | Reset any task that has been `Running` longer than `timeout_s` back to `Ready` and re-enqueue it. |
64
+
65
+ Within a queue, higher `priority` values are dispatched first.
66
+ A worker polls `TaskGet` across the queues it cares about, runs the work,
67
+ and reports back with `TaskDone`.
68
+ `Requeue` provides fault tolerance:
69
+ if a worker crashes without completing its task,
70
+ a periodic `Requeue` call makes that task available to another worker.
71
+
72
+ ## Building the server
73
+
74
+ Dependencies are managed with [Conan](https://conan.io/)
75
+ and the build is driven by CMake.
76
+
77
+ ```sh
78
+ conan install .
79
+ . build/Release/generators/conanbuild.sh
80
+ cmake -S . -B build/Release \
81
+ -DCMAKE_BUILD_TYPE=Release \
82
+ -DCMAKE_TOOLCHAIN_FILE=generators/conan_toolchain.cmake
83
+ cmake --build build/Release --parallel
84
+ ```
85
+
86
+ A reproducible container build is defined in
87
+ `scripts/apptainer/ds-server.def`.
88
+
89
+ ### Running
90
+
91
+ ```sh
92
+ ds-server --address 0.0.0.0:5051
93
+ ```
94
+
95
+ Run `ds-server --help` for the full argument list.
96
+
97
+ ## Python client
98
+
99
+ ```sh
100
+ pip install . # installs the ds-service-client package
101
+ ```
102
+
103
+ ```python
104
+ from ds_service_client import Client, TaskState
105
+
106
+ client = Client("127.0.0.1:5051") # or set DS_SERVER_ADDRESS and call Client()
107
+
108
+ # Key-value map
109
+ client.map_set("greeting", b"hello")
110
+ assert client.map_get("greeting") == b"hello"
111
+
112
+ # Task queue
113
+ client.task_add("job-1", queue="work", priority=1.0, function=b"...", input=b"...")
114
+
115
+ task = client.task_get(worker_id="worker-a", queue="work")
116
+ # ... do the work ...
117
+ client.task_done(task.task_id, output=b"result")
118
+
119
+ status = client.task_status("job-1")
120
+ assert status.state == TaskState.Complete
121
+ ```
122
+
123
+ If `Client()` is constructed without an address, it reads the server address
124
+ from the `DS_SERVER_ADDRESS` environment variable.
125
+
126
+ ## License
127
+
128
+ MIT -- see [LICENSE](LICENSE).
@@ -0,0 +1,49 @@
1
+ # type: ignore
2
+ from conan import ConanFile
3
+ from conan.tools.cmake import cmake_layout, CMakeDeps, CMakeToolchain, CMake
4
+
5
+
6
+ class DsServiceRecipe(ConanFile):
7
+ name = "ds-service"
8
+ version = "main"
9
+
10
+ settings = "os", "compiler", "build_type", "arch"
11
+ options = {"shared": [True, False], "fPIC": [True, False]}
12
+ default_options = {"shared": False, "fPIC": True}
13
+
14
+ exports_sources = "CMakeLists.txt", "cpp/*", "misc/*"
15
+
16
+ def layout(self):
17
+ cmake_layout(self)
18
+
19
+ def requirements(self):
20
+ self.requires("spdlog/1.17.0")
21
+ self.requires("argparse/3.2")
22
+ self.requires("parallel-hashmap/2.0.0")
23
+
24
+ self.requires("grpc/1.78.1")
25
+ self.requires("protobuf/6.33.5")
26
+
27
+ def build_requirements(self):
28
+ self.tool_requires("grpc/1.78.1")
29
+ self.tool_requires("protobuf/6.33.5")
30
+ self.tool_requires("cmake/4.3.0")
31
+
32
+ def generate(self):
33
+ deps = CMakeDeps(self)
34
+ deps.generate()
35
+
36
+ toolchain = CMakeToolchain(self)
37
+ toolchain.generate()
38
+
39
+ def build(self):
40
+ cmake = CMake(self)
41
+ cmake.configure()
42
+ cmake.build()
43
+
44
+ def package(self):
45
+ cmake = CMake(self)
46
+ cmake.install()
47
+
48
+ def package_info(self):
49
+ self.cpp_info.libs = ["ds-service-grpc"]