dory-processor-sdk 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dory/__init__.py +101 -0
- dory/auth/__init__.py +10 -0
- dory/auth/oauth2.py +153 -0
- dory/auto_instrument.py +142 -0
- dory/cli/__init__.py +5 -0
- dory/cli/main.py +137 -0
- dory/cli/templates.py +123 -0
- dory/config/__init__.py +23 -0
- dory/config/defaults.py +24 -0
- dory/config/loader.py +430 -0
- dory/config/presets.py +73 -0
- dory/config/schema.py +84 -0
- dory/core/__init__.py +27 -0
- dory/core/app.py +434 -0
- dory/core/context.py +209 -0
- dory/core/lifecycle.py +214 -0
- dory/core/meta.py +121 -0
- dory/core/modes.py +479 -0
- dory/core/processor.py +564 -0
- dory/core/signals.py +122 -0
- dory/decorators.py +142 -0
- dory/edge/__init__.py +88 -0
- dory/edge/adaptive.py +644 -0
- dory/edge/detector.py +546 -0
- dory/edge/fencing.py +488 -0
- dory/edge/heartbeat.py +598 -0
- dory/edge/role.py +419 -0
- dory/errors/__init__.py +139 -0
- dory/errors/classification.py +362 -0
- dory/errors/codes.py +498 -0
- dory/geo/__init__.py +40 -0
- dory/geo/geolocalizer.py +1034 -0
- dory/health/__init__.py +12 -0
- dory/health/probes.py +210 -0
- dory/health/server.py +635 -0
- dory/k8s/__init__.py +80 -0
- dory/k8s/annotation_watcher.py +184 -0
- dory/k8s/client.py +251 -0
- dory/k8s/labels.py +505 -0
- dory/k8s/pod_metadata.py +182 -0
- dory/logging/__init__.py +9 -0
- dory/logging/logger.py +148 -0
- dory/metrics/__init__.py +7 -0
- dory/metrics/collector.py +301 -0
- dory/middleware/__init__.py +46 -0
- dory/middleware/connection_tracker.py +608 -0
- dory/middleware/request_id.py +325 -0
- dory/middleware/request_tracker.py +511 -0
- dory/migration/__init__.py +33 -0
- dory/migration/configmap.py +232 -0
- dory/migration/s3_store.py +594 -0
- dory/migration/serialization.py +135 -0
- dory/migration/state_manager.py +286 -0
- dory/migration/transfer.py +382 -0
- dory/monitoring/__init__.py +29 -0
- dory/monitoring/opentelemetry.py +489 -0
- dory/output/__init__.py +31 -0
- dory/output/envelope.py +137 -0
- dory/output/formatter.py +113 -0
- dory/output/rabbitmq.py +632 -0
- dory/output/routing.py +318 -0
- dory/output/validator.py +199 -0
- dory/py.typed +2 -0
- dory/recovery/__init__.py +60 -0
- dory/recovery/golden_image.py +487 -0
- dory/recovery/golden_snapshot.py +713 -0
- dory/recovery/golden_validator.py +518 -0
- dory/recovery/partial_recovery.py +482 -0
- dory/recovery/recovery_decision.py +242 -0
- dory/recovery/restart_detector.py +142 -0
- dory/recovery/state_validator.py +183 -0
- dory/resilience/__init__.py +45 -0
- dory/resilience/circuit_breaker.py +457 -0
- dory/resilience/retry.py +389 -0
- dory/simple.py +342 -0
- dory/types.py +68 -0
- dory/utils/__init__.py +31 -0
- dory/utils/errors.py +59 -0
- dory/utils/retry.py +115 -0
- dory/utils/timeout.py +80 -0
- dory_processor_sdk-0.0.1.dist-info/METADATA +424 -0
- dory_processor_sdk-0.0.1.dist-info/RECORD +86 -0
- dory_processor_sdk-0.0.1.dist-info/WHEEL +5 -0
- dory_processor_sdk-0.0.1.dist-info/entry_points.txt +2 -0
- dory_processor_sdk-0.0.1.dist-info/licenses/LICENSE +201 -0
- dory_processor_sdk-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dory-processor-sdk
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Python SDK for building stateful processors with zero-downtime migration, auto-initialization, and smart instrumentation
|
|
5
|
+
Author-email: Dory Team <xguo2016@fau.edu>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Keywords: kubernetes,stateful,migration,orchestration,sdk
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: aiohttp>=3.8.0
|
|
23
|
+
Requires-Dist: pydantic>=2.0.0
|
|
24
|
+
Requires-Dist: PyYAML>=6.0
|
|
25
|
+
Provides-Extra: production
|
|
26
|
+
Requires-Dist: kubernetes>=28.0.0; extra == "production"
|
|
27
|
+
Requires-Dist: boto3>=1.28.0; extra == "production"
|
|
28
|
+
Requires-Dist: opentelemetry-api>=1.20.0; extra == "production"
|
|
29
|
+
Requires-Dist: opentelemetry-sdk>=1.20.0; extra == "production"
|
|
30
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.20.0; extra == "production"
|
|
31
|
+
Requires-Dist: aio-pika>=9.0.0; extra == "production"
|
|
32
|
+
Requires-Dist: redis>=4.5.0; extra == "production"
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
38
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
39
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# Dory Processor SDK
|
|
43
|
+
|
|
44
|
+
A production-ready Python SDK for building **stateful, fault-tolerant processors** on Kubernetes with zero-downtime migration, automatic state persistence, and comprehensive observability.
|
|
45
|
+
|
|
46
|
+
[](https://pypi.org/project/dory-processor-sdk/)
|
|
47
|
+
[](https://www.python.org/downloads/)
|
|
48
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
49
|
+
|
|
50
|
+
> **Part of the Dory platform.** The Dory Processor SDK is the official Python SDK for building processors that run on the **Dory platform**. It is designed to work together with the Dory orchestrator, which manages pod lifecycle, zero-downtime migration, state transfer, and edge failover for your processors.
|
|
51
|
+
|
|
52
|
+
## Why Dory Processor SDK?
|
|
53
|
+
|
|
54
|
+
| Challenge | Without Dory | With Dory Processor SDK |
|
|
55
|
+
|-----------|--------------|---------------|
|
|
56
|
+
| Pod termination | State lost, start from scratch | State auto-saved to ConfigMap, restored on new pod |
|
|
57
|
+
| Node drain/maintenance | Downtime, manual intervention | Zero-downtime migration with state transfer |
|
|
58
|
+
| Transient failures | DIY retry logic | Built-in retry with exponential backoff |
|
|
59
|
+
| Cascading failures | Service degradation | Circuit breakers protect dependencies |
|
|
60
|
+
| Debugging distributed systems | Scattered logs | OpenTelemetry tracing across services |
|
|
61
|
+
| Health monitoring | Custom implementation | Built-in `/health`, `/ready`, `/metrics` |
|
|
62
|
+
|
|
63
|
+
## Features
|
|
64
|
+
|
|
65
|
+
### Core
|
|
66
|
+
- **`@stateful` decorator** - Automatic state persistence and restoration
|
|
67
|
+
- **`DoryApp`** - Application lifecycle management
|
|
68
|
+
- **`BaseProcessor`** - Base class with built-in hooks
|
|
69
|
+
- **`ExecutionContext`** - Pod metadata, logging, shutdown detection
|
|
70
|
+
|
|
71
|
+
### Resilience
|
|
72
|
+
- **Circuit Breaker** - Prevent cascading failures with configurable thresholds
|
|
73
|
+
- **Retry with Backoff** - Exponential backoff with jitter and retry budgets
|
|
74
|
+
- **Error Classification** - Intelligent error categorization (transient, permanent, resource)
|
|
75
|
+
|
|
76
|
+
### Observability
|
|
77
|
+
- **OpenTelemetry** - Distributed tracing with automatic span creation
|
|
78
|
+
- **Prometheus Metrics** - Built-in `/metrics` endpoint
|
|
79
|
+
- **Structured Logging** - JSON logs with request context
|
|
80
|
+
|
|
81
|
+
### State Management
|
|
82
|
+
- **ConfigMap Backend** - Persist state in Kubernetes ConfigMaps
|
|
83
|
+
- **S3 Backend** - Store large state in S3 (with offline buffering)
|
|
84
|
+
- **Local Backend** - File-based storage for development
|
|
85
|
+
- **State Versioning** - Forward/backward compatible state formats
|
|
86
|
+
|
|
87
|
+
### Recovery
|
|
88
|
+
- **Restart Detection** - Detect rapid restart loops
|
|
89
|
+
- **State Validation** - Validate state integrity before restore
|
|
90
|
+
- **Golden Snapshots** - Create known-good state checkpoints
|
|
91
|
+
- **Partial Recovery** - Recover individual fields when full restore fails
|
|
92
|
+
- **Golden Image Reset** - Factory reset capability
|
|
93
|
+
|
|
94
|
+
### Middleware
|
|
95
|
+
- **Request Tracking** - Track request lifecycle and metrics
|
|
96
|
+
- **Request ID Propagation** - Automatic request ID across service calls
|
|
97
|
+
- **Connection Tracking** - Monitor connection lifecycle
|
|
98
|
+
|
|
99
|
+
### Edge Support
|
|
100
|
+
- **Fencing Manager** - Split-brain prevention for edge deployments
|
|
101
|
+
- **Heartbeat Manager** - Connectivity monitoring
|
|
102
|
+
- **Role Manager** - Primary/standby failover
|
|
103
|
+
|
|
104
|
+
## Installation
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pip install dory-processor-sdk # Core SDK (aiohttp, pydantic, PyYAML)
|
|
108
|
+
pip install dory-processor-sdk[production] # EKS deployment (K8s, S3, OpenTelemetry, RabbitMQ, Redis)
|
|
109
|
+
pip install dory-processor-sdk[dev] # Test/lint tooling (pytest, mypy, ruff, black)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> Only two optional-dependency extras exist: `[production]` and `[dev]`. The Kubernetes, S3 (boto3),
|
|
113
|
+
> OpenTelemetry, RabbitMQ (aio-pika) and Redis dependencies are all bundled inside `[production]`.
|
|
114
|
+
|
|
115
|
+
## Quick Start
|
|
116
|
+
|
|
117
|
+
### Minimal Example (7 lines)
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from dory import DoryApp, BaseProcessor, stateful
|
|
121
|
+
|
|
122
|
+
class MyApp(BaseProcessor):
|
|
123
|
+
counter = stateful(0) # Automatically saved and restored
|
|
124
|
+
|
|
125
|
+
async def run(self):
|
|
126
|
+
async for _ in self.run_loop(interval=1):
|
|
127
|
+
self.counter += 1
|
|
128
|
+
print(f"Count: {self.counter}")
|
|
129
|
+
|
|
130
|
+
if __name__ == "__main__":
|
|
131
|
+
DoryApp().run(MyApp)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### With Resilience Features
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from dory import DoryApp, BaseProcessor, stateful
|
|
138
|
+
from dory.resilience import CircuitBreaker, retry_with_backoff
|
|
139
|
+
from dory.monitoring import create_span
|
|
140
|
+
|
|
141
|
+
class MyApp(BaseProcessor):
|
|
142
|
+
counter = stateful(0)
|
|
143
|
+
db_breaker = CircuitBreaker(name="database", failure_threshold=5)
|
|
144
|
+
|
|
145
|
+
@retry_with_backoff(max_attempts=3)
|
|
146
|
+
async def fetch_data(self):
|
|
147
|
+
with create_span("fetch_data"):
|
|
148
|
+
return await self.db_breaker.call(self.database.query)
|
|
149
|
+
|
|
150
|
+
async def run(self):
|
|
151
|
+
async for _ in self.run_loop(interval=1):
|
|
152
|
+
try:
|
|
153
|
+
data = await self.fetch_data()
|
|
154
|
+
self.counter += 1
|
|
155
|
+
except Exception as e:
|
|
156
|
+
self.context.logger().error(f"Failed: {e}")
|
|
157
|
+
|
|
158
|
+
if __name__ == "__main__":
|
|
159
|
+
DoryApp().run(MyApp)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Function-Based API
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from dory.simple import processor, state
|
|
166
|
+
|
|
167
|
+
counter = state(0)
|
|
168
|
+
|
|
169
|
+
@processor
|
|
170
|
+
async def main(ctx):
|
|
171
|
+
async for _ in ctx.run_loop(interval=1):
|
|
172
|
+
counter.value += 1
|
|
173
|
+
ctx.logger().info(f"Count: {counter.value}")
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Configuration
|
|
177
|
+
|
|
178
|
+
### Zero-Config (Recommended)
|
|
179
|
+
|
|
180
|
+
**No configuration file needed!** The SDK auto-detects your environment:
|
|
181
|
+
|
|
182
|
+
| Environment | Auto-Configuration |
|
|
183
|
+
|-------------|-------------------|
|
|
184
|
+
| **Kubernetes** | Production preset, ConfigMap state, JSON logs, port 8080 |
|
|
185
|
+
| **Local** | Development preset, local file state, colored logs, auto-port |
|
|
186
|
+
|
|
187
|
+
Just run your code - the SDK handles everything.
|
|
188
|
+
|
|
189
|
+
**Run multiple apps locally?** Each auto-selects an available port (no conflicts).
|
|
190
|
+
|
|
191
|
+
### Optional: dory.yaml
|
|
192
|
+
|
|
193
|
+
Only create `dory.yaml` if you need custom settings. `DoryConfig` recognizes
|
|
194
|
+
exactly **five** keys (everything else is silently ignored via `extra="ignore"`):
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
startup_timeout_sec: 30 # 1-300, default 30
|
|
198
|
+
shutdown_timeout_sec: 30 # 1-300, default 30
|
|
199
|
+
health_port: 8080 # 0-65535, default 8080 (0 = auto-select)
|
|
200
|
+
state_backend: configmap # configmap | pvc | s3 | local, default configmap
|
|
201
|
+
log_level: INFO # DEBUG | INFO | WARNING | ERROR | CRITICAL, default INFO
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
> **Not supported in `dory.yaml`:** retry, circuit-breaker, and OpenTelemetry tuning are
|
|
205
|
+
> **not** config-file keys. Any `retry:`, `circuit_breaker:`, or `opentelemetry:` block is
|
|
206
|
+
> silently dropped. Configure those components in code (see the resilience examples below)
|
|
207
|
+
> or via their constructor arguments in `BaseProcessor.__init__`.
|
|
208
|
+
|
|
209
|
+
See `docs/08-configuration.md` for the authoritative config reference.
|
|
210
|
+
|
|
211
|
+
### Environment Variables
|
|
212
|
+
|
|
213
|
+
| Variable | Default | Description |
|
|
214
|
+
|----------|---------|-------------|
|
|
215
|
+
| `DORY_HEALTH_PORT` | 8080 | Health server port |
|
|
216
|
+
| `DORY_STATE_BACKEND` | configmap | State storage (configmap/pvc/s3/local) |
|
|
217
|
+
| `DORY_LOG_LEVEL` | INFO | Log level |
|
|
218
|
+
| `DORY_STARTUP_TIMEOUT_SEC` | 30 | Startup timeout |
|
|
219
|
+
| `DORY_SHUTDOWN_TIMEOUT_SEC` | 30 | Shutdown timeout |
|
|
220
|
+
|
|
221
|
+
See `docs/08-configuration.md` for the full runtime env var catalog.
|
|
222
|
+
|
|
223
|
+
## API Reference
|
|
224
|
+
|
|
225
|
+
### BaseProcessor
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
class MyApp(BaseProcessor):
|
|
229
|
+
# Stateful fields (auto-saved/restored)
|
|
230
|
+
counter = stateful(0)
|
|
231
|
+
data = stateful(dict)
|
|
232
|
+
|
|
233
|
+
async def startup(self):
|
|
234
|
+
"""Called once on startup (optional)"""
|
|
235
|
+
pass
|
|
236
|
+
|
|
237
|
+
async def run(self):
|
|
238
|
+
"""Main processing loop (required)"""
|
|
239
|
+
async for i in self.run_loop(interval=1):
|
|
240
|
+
self.counter += 1
|
|
241
|
+
|
|
242
|
+
async def shutdown(self):
|
|
243
|
+
"""Called on graceful shutdown (optional)"""
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
# Fault handling hooks (optional)
|
|
247
|
+
async def on_state_restore_failed(self, error: Exception):
|
|
248
|
+
"""Called when state restoration fails"""
|
|
249
|
+
pass
|
|
250
|
+
|
|
251
|
+
async def on_rapid_restart_detected(self, restart_count: int):
|
|
252
|
+
"""Called when rapid restart loop detected"""
|
|
253
|
+
pass
|
|
254
|
+
|
|
255
|
+
def reset_caches(self):
|
|
256
|
+
"""Called on golden image reset"""
|
|
257
|
+
pass
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Circuit Breaker
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
from dory.resilience import CircuitBreaker, CircuitState
|
|
264
|
+
|
|
265
|
+
# Create circuit breaker
|
|
266
|
+
breaker = CircuitBreaker(
|
|
267
|
+
name="database",
|
|
268
|
+
failure_threshold=5, # Open after 5 failures
|
|
269
|
+
success_threshold=2, # Close after 2 successes in half-open
|
|
270
|
+
timeout_seconds=30.0, # Seconds before trying half-open
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Use with async call
|
|
274
|
+
result = await breaker.call(async_function, arg1, arg2)
|
|
275
|
+
|
|
276
|
+
# Check state
|
|
277
|
+
if breaker.state == CircuitState.OPEN:
|
|
278
|
+
print("Circuit is open, requests will fail fast")
|
|
279
|
+
|
|
280
|
+
# Manual control (both are async coroutines)
|
|
281
|
+
await breaker.open() # Force open
|
|
282
|
+
await breaker.reset() # Force closed
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Retry with Backoff
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
from dory.resilience import retry_with_backoff, RetryPolicy
|
|
289
|
+
|
|
290
|
+
# Decorator usage
|
|
291
|
+
@retry_with_backoff(max_attempts=3, initial_delay=0.1)
|
|
292
|
+
async def flaky_operation():
|
|
293
|
+
return await api.call()
|
|
294
|
+
|
|
295
|
+
# With custom policy
|
|
296
|
+
policy = RetryPolicy(
|
|
297
|
+
max_attempts=5,
|
|
298
|
+
initial_delay=0.1,
|
|
299
|
+
multiplier=2.0,
|
|
300
|
+
max_delay=30.0,
|
|
301
|
+
jitter=True
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
@retry_with_backoff(policy=policy)
|
|
305
|
+
async def custom_retry():
|
|
306
|
+
pass
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Error Classification
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
from dory.errors import ErrorClassifier, ErrorType
|
|
313
|
+
|
|
314
|
+
classifier = ErrorClassifier()
|
|
315
|
+
|
|
316
|
+
try:
|
|
317
|
+
await operation()
|
|
318
|
+
except Exception as e:
|
|
319
|
+
result = classifier.classify(e)
|
|
320
|
+
|
|
321
|
+
# ClassificationResult fields: error_type, recommended_action,
|
|
322
|
+
# retryable, severity, details
|
|
323
|
+
if result.retryable:
|
|
324
|
+
# Retry the operation
|
|
325
|
+
await retry_operation()
|
|
326
|
+
else:
|
|
327
|
+
# Don't retry, log and alert
|
|
328
|
+
logger.error(
|
|
329
|
+
f"Non-retryable error: {e} "
|
|
330
|
+
f"(action={result.recommended_action}, severity={result.severity})"
|
|
331
|
+
)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### OpenTelemetry
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
from dory.monitoring import create_span, add_span_attributes, trace_function
|
|
338
|
+
|
|
339
|
+
# Context manager
|
|
340
|
+
with create_span("database_query", {"table": "users"}):
|
|
341
|
+
result = await db.query("SELECT * FROM users")
|
|
342
|
+
|
|
343
|
+
# Decorator
|
|
344
|
+
@trace_function("process_item")
|
|
345
|
+
async def process_item(item):
|
|
346
|
+
add_span_attributes({"item_id": item.id})
|
|
347
|
+
return await transform(item)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### ExecutionContext
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
async def run(self):
|
|
354
|
+
ctx = self.context
|
|
355
|
+
|
|
356
|
+
# Logging
|
|
357
|
+
ctx.logger().info("Processing started")
|
|
358
|
+
|
|
359
|
+
# Pod metadata
|
|
360
|
+
print(f"Pod: {ctx.pod_name}")
|
|
361
|
+
print(f"Namespace: {ctx.pod_namespace}")
|
|
362
|
+
print(f"Processor ID: {ctx.processor_id}")
|
|
363
|
+
|
|
364
|
+
# Shutdown detection
|
|
365
|
+
while not ctx.is_shutdown_requested():
|
|
366
|
+
if ctx.is_migration_imminent():
|
|
367
|
+
print("Migration coming, saving state...")
|
|
368
|
+
await process()
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## HTTP Endpoints
|
|
372
|
+
|
|
373
|
+
| Endpoint | Method | Description |
|
|
374
|
+
|----------|--------|-------------|
|
|
375
|
+
| `/health` | GET | Liveness probe (is process alive?) |
|
|
376
|
+
| `/ready` | GET | Readiness probe (ready to serve?) |
|
|
377
|
+
| `/metrics` | GET | Prometheus metrics |
|
|
378
|
+
| `/state` | GET | Export current state (Bearer-authenticated) |
|
|
379
|
+
| `/state` | POST | Import/restore state (Bearer-authenticated) |
|
|
380
|
+
| `/prestop` | GET | PreStop hook handler |
|
|
381
|
+
| `/` | GET | Service info + SDK version |
|
|
382
|
+
|
|
383
|
+
## CLI Tool
|
|
384
|
+
|
|
385
|
+
The CLI provides two commands: `dory init` and `dory validate`.
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
# Initialize a new project (creates main.py + Dockerfile)
|
|
389
|
+
dory init my-app
|
|
390
|
+
dory init my-app -o ./my-app -f # custom output dir, overwrite existing
|
|
391
|
+
|
|
392
|
+
# Validate configuration (loads dory.yaml + env overrides and prints settings)
|
|
393
|
+
dory validate
|
|
394
|
+
dory validate -c path/to/dory.yaml
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**`dory init` flags:** `name` (positional), `-o/--output`, `-i/--image` (accepted but
|
|
398
|
+
currently unused), `-f/--force`.
|
|
399
|
+
**`dory validate` flags:** `-c/--config`.
|
|
400
|
+
|
|
401
|
+
## State Migration Flow
|
|
402
|
+
|
|
403
|
+
### Pod Shutdown
|
|
404
|
+
```
|
|
405
|
+
1. Kubernetes sends SIGTERM / calls /prestop
|
|
406
|
+
2. SDK marks processor as not-ready
|
|
407
|
+
3. SDK saves state to ConfigMap
|
|
408
|
+
4. Your shutdown() is called
|
|
409
|
+
5. Pod terminates
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Pod Startup
|
|
413
|
+
```
|
|
414
|
+
1. New pod starts
|
|
415
|
+
2. SDK checks for existing state in ConfigMap
|
|
416
|
+
3. Your startup() is called
|
|
417
|
+
4. SDK restores state (calls restore_state or sets @stateful fields)
|
|
418
|
+
5. SDK marks processor as ready
|
|
419
|
+
6. Your run() starts
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## License
|
|
423
|
+
|
|
424
|
+
Apache 2.0
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
dory/__init__.py,sha256=3cLiK6jegQzL8iyeBAuj1C0vg1lmTtn_LV0e_DvyznU,2267
|
|
2
|
+
dory/auto_instrument.py,sha256=8vKhOi0nXFuG0DN7KIFC20rzfNOZtbl0oAMxFvXd3hA,4689
|
|
3
|
+
dory/decorators.py,sha256=q2eCjlP4LaTjpmJZt30pzx1J1mhuoQvpm4JrHkq8IsY,4384
|
|
4
|
+
dory/py.typed,sha256=emnf6qe_Z56R1XIXFqsN16wUecXbya2eYibnwMfH2Ng,93
|
|
5
|
+
dory/simple.py,sha256=hcwEyime_aGe1mN4lRToq30cT0rQguk1IFITDiXUHP0,9899
|
|
6
|
+
dory/types.py,sha256=1d3tKQLAPrklY6SJzQDQ8A3600GVfI9X_Z4gjbGAwJ4,2263
|
|
7
|
+
dory/auth/__init__.py,sha256=7ZuM0HBpoCXzH1C4dTfZfs3y-VN0ovRT74TPiSNbiYw,206
|
|
8
|
+
dory/auth/oauth2.py,sha256=FbYY5d4fquGT18jICllHHVYo2F8rDahFYrq78jiMoHM,4967
|
|
9
|
+
dory/cli/__init__.py,sha256=gz5g4VP5enbVTadWqqS_1Ys8-xMqPEmGB8AerouysVc,78
|
|
10
|
+
dory/cli/main.py,sha256=836w_v6EpWvjS_-nDT1jGh7TGNZ46Zyl7Zo2XrzTMZE,3459
|
|
11
|
+
dory/cli/templates.py,sha256=o-rX9UGSc6tfXZETAgGKeXqRqOgH5-hejIym_s-FHeE,3096
|
|
12
|
+
dory/config/__init__.py,sha256=mGgsgkFclIVyKs69kKZzRYe2FjC3JtzD9etIQvzLLRI,530
|
|
13
|
+
dory/config/defaults.py,sha256=odMflJY1egp1NpZry8fkvt6YEuMABOavGxlVX3V-ENQ,560
|
|
14
|
+
dory/config/loader.py,sha256=PC7l3q5SsEK8O0CXNYd8TR7Af3JB0amfmCnQnkPaIzM,14353
|
|
15
|
+
dory/config/presets.py,sha256=HtSQjskMr34wZSVprkSnvqaZ03-rmzoyC1uzHB_zmlg,1994
|
|
16
|
+
dory/config/schema.py,sha256=v9kZnrty3R_M1GDAPj2QibwjHIj3_nxVB96eY8jeCFQ,2601
|
|
17
|
+
dory/core/__init__.py,sha256=qN3cpZT2yqCbo_D7dcJYsLErY4LI-v1Na2ULq_wLt1g,614
|
|
18
|
+
dory/core/app.py,sha256=O-imXOWbTAvty8dmkyYAioK6FaF8sQkx6IGTngYNdvg,15688
|
|
19
|
+
dory/core/context.py,sha256=92tIPIG4qCYRAyk0i-bFw2tZxY_1E1rFAJ82cSNIIhQ,7186
|
|
20
|
+
dory/core/lifecycle.py,sha256=IXGVmoRbwN0s-yuz82SvyNduTusP53-U0KB7avUNqpk,6783
|
|
21
|
+
dory/core/meta.py,sha256=AkTzGrnClY75BIYPYTn7IpZKTvg5MV5-rprHjYC9Xu0,3827
|
|
22
|
+
dory/core/modes.py,sha256=KpJioN170NyUfrKhyHPhAnNx4GP5inf57xetYBQeKLg,16658
|
|
23
|
+
dory/core/processor.py,sha256=SEHLt5AWaOR1rESezWv-xs-cU0zJ-8mUWtmxDb_TEe4,19194
|
|
24
|
+
dory/core/signals.py,sha256=cwsjvbKPT_-iyx6I2cKM8XedreEULfpUpfVoV1LTBx0,4205
|
|
25
|
+
dory/edge/__init__.py,sha256=3EbWFzvaAbXRTboTynMiN9mh_D2pgqdIwNloOl7PwOI,1927
|
|
26
|
+
dory/edge/adaptive.py,sha256=9weH87x4MX6mXE59VrexMejmXHnMg2ihuBABn6PkFbs,22652
|
|
27
|
+
dory/edge/detector.py,sha256=qcg4EghVRNdELa-d29ryDs48Otxp23pkW9Ed5DvMp5E,17602
|
|
28
|
+
dory/edge/fencing.py,sha256=1hfeV8bPvsqF-9LJ-ZVOkVL_n_2Li1dpRllnJSXIsIc,15557
|
|
29
|
+
dory/edge/heartbeat.py,sha256=74JR-RzQOv6Ok_F4h3HXMGo0YmJ_VcZtjYxkncnFklk,20227
|
|
30
|
+
dory/edge/role.py,sha256=YtwOJKnvGUWzfODkSC6j-gODiB0QuNdUpJZ7zGnkxAw,13026
|
|
31
|
+
dory/errors/__init__.py,sha256=e3-v8s1ss2BdsJoSAhlh11MkjBy2wErFOgfoBm5Kg-Q,2980
|
|
32
|
+
dory/errors/classification.py,sha256=WDXBKisd6rIkvXx13uo7G8PL7pf--6F07f7pELmvQyI,10826
|
|
33
|
+
dory/errors/codes.py,sha256=N3YXR3joIDjuR95jSKYKX1Xd_FoBflaXf6eWZxJqDYc,15034
|
|
34
|
+
dory/geo/__init__.py,sha256=81CkHMGSpDno0g9NWnIeRAa1xZYSyOu97NrgKHVDx2A,1000
|
|
35
|
+
dory/geo/geolocalizer.py,sha256=HMyw7uryn5W8GxPspPtrhxsZtGe6ZaJuKP5dDBma9Hk,33480
|
|
36
|
+
dory/health/__init__.py,sha256=Ls2Vf_lOfpzFKhw0oB6vPPg55i16f38v0Le8oFzphgY,294
|
|
37
|
+
dory/health/probes.py,sha256=x4eLqj56Pm1RdqUEAwYlafA_UkjICFuoRPAtKAmj1Gg,6355
|
|
38
|
+
dory/health/server.py,sha256=1XVrGF-DjQuZkK5ODSGPj2OChiO7Ur6qiqiGyC3ULXs,23136
|
|
39
|
+
dory/k8s/__init__.py,sha256=q1tshZhemvo-9Yx3vrjsjuwAFDpZAfiVMRwHtvkSxMQ,1804
|
|
40
|
+
dory/k8s/annotation_watcher.py,sha256=jokUaU1sXMEHG1OO6E6IvoE_9nU9F9fPgbACskiv7b8,6019
|
|
41
|
+
dory/k8s/client.py,sha256=5qeortOayciLGYkM1eqNLYDt1nZBDMrvLfRvtHfEKy0,6682
|
|
42
|
+
dory/k8s/labels.py,sha256=kykB9oFhpOHCZECnR6wsjg24pfo4SqbtA40k_NRhL7E,16480
|
|
43
|
+
dory/k8s/pod_metadata.py,sha256=GdbKwftBeJ7lRA69s8qjs1Qrz0Mx5dRpgdN3lhgl2pk,5487
|
|
44
|
+
dory/logging/__init__.py,sha256=rvb6Gdc2KzhwtEOSiKz8_kgzW2oztMLX4zHivkm2fkQ,193
|
|
45
|
+
dory/logging/logger.py,sha256=v5qZsdo_fNfwjVz2Cn6k2iGzg2FVafqqqZCOvesVdus,4286
|
|
46
|
+
dory/metrics/__init__.py,sha256=jGrcBgK6yvQi9V3qaS1ezPb3Lu9zN4HvH3NLSzemXhM,129
|
|
47
|
+
dory/metrics/collector.py,sha256=-8QC29SVg63j6vm_J3ZOIBi805NJesWEhHdHMK5PjXc,9181
|
|
48
|
+
dory/middleware/__init__.py,sha256=GbItxdD9lqixpfPkXr-CY0zpksw0d_m7WrhBKNWFDqM,952
|
|
49
|
+
dory/middleware/connection_tracker.py,sha256=gHadUF9EP3_sGt1Pp8rerytoLaDpEjIoQq50ZJb-s3U,19154
|
|
50
|
+
dory/middleware/request_id.py,sha256=A4W5HtC3m5oVmNdXyeILktKvm_K415zitQ6O4ARn5N4,9180
|
|
51
|
+
dory/middleware/request_tracker.py,sha256=942IFGU2nCGXaj1ez0uHifPZRq8Bc74lEpkgKeuZ-9E,16439
|
|
52
|
+
dory/migration/__init__.py,sha256=ezRGcSvWrRXZW1xNPWXZ_Uv4mAO7szrDn79a0AvbKfk,906
|
|
53
|
+
dory/migration/configmap.py,sha256=nWzhFH7uGMzXz4CXkKmF3Y34PSmbdoNuH3uD-53TaB8,6959
|
|
54
|
+
dory/migration/s3_store.py,sha256=7UqQqyUpn557VEl2Tvy3nReCRDGZjSj3E5shbKvmC3o,19678
|
|
55
|
+
dory/migration/serialization.py,sha256=Npok9bqvns51LuzN_T61y11KgE-J_iyBAHRsyVreZO4,3690
|
|
56
|
+
dory/migration/state_manager.py,sha256=kjcp8wmM_NWrzV0h5QxVKMuuxzUv5YdiYuWLdHUg610,9936
|
|
57
|
+
dory/migration/transfer.py,sha256=WDw02y4N50-j8tpJhSp1wR6hVCa9vg1i6nHLQSuq4HI,11906
|
|
58
|
+
dory/monitoring/__init__.py,sha256=vzxjU_u63Y9LYSrME1DYbQmQBBM7z8wc51pam-krzhk,562
|
|
59
|
+
dory/monitoring/opentelemetry.py,sha256=zhF4M4F00Q7BOi3Nx2FXM7jJMevSmDBbgxeGq_0pMVo,14446
|
|
60
|
+
dory/output/__init__.py,sha256=EtY8myQZOCKAoiBEymCUOqnEEEtCP7pTeLO6lR9eA8o,790
|
|
61
|
+
dory/output/envelope.py,sha256=tmmxkwsJ9NUZb3tDriVt6PDRPPeDXmLS2N4ondXrDI0,4070
|
|
62
|
+
dory/output/formatter.py,sha256=7mHrHrr0q_u_-A2Ap1V3rvOgtYSCMRfESTp8B0Xe7c4,3201
|
|
63
|
+
dory/output/rabbitmq.py,sha256=wd1buozIiiykysXqZwEaI8970upn_IkhzE0BopmxPOY,23033
|
|
64
|
+
dory/output/routing.py,sha256=YJZPvlsoviV0QQT8raNTnD9C0mo7BlIyevQgJWVfhDM,10480
|
|
65
|
+
dory/output/validator.py,sha256=WapZzsX2_aLaY98tGmo4Ia7KA338kpnXJSmukuDa4Fs,6254
|
|
66
|
+
dory/recovery/__init__.py,sha256=PsSrR8kM1hAzVtb6aFw0ub1jMxd-TXxaEwJf3MhXNWc,1641
|
|
67
|
+
dory/recovery/golden_image.py,sha256=wfSezDFLJNYCLBOY6vO2gPW_QAVfzjx-4o0hvEBbEyU,14703
|
|
68
|
+
dory/recovery/golden_snapshot.py,sha256=PMXIItXMtbNYd7QaSx47ItWQneqxWSlfa1ytZhXHP4Y,22890
|
|
69
|
+
dory/recovery/golden_validator.py,sha256=zi9EBkeRqytQxWk4yYleJy6lYPSIRqO2UuYzRrjgwCI,17426
|
|
70
|
+
dory/recovery/partial_recovery.py,sha256=eNcDLHZ3_Gj83AOW8brhXWWhNx5nufe4PHI1M2hMcW4,15940
|
|
71
|
+
dory/recovery/recovery_decision.py,sha256=SBe3bhLBOaGPGFOAVtcrG5zR3R6Kmhznsd5NuZaQiBE,8026
|
|
72
|
+
dory/recovery/restart_detector.py,sha256=zm926crLYzt8gkZnv-B3PT4ZeQug_LC1g-gsr40Qe6k,4484
|
|
73
|
+
dory/recovery/state_validator.py,sha256=GCv_KzsPMk6PpPO0LQoQhGqvsN57IStzTwqQ8efxA9w,5397
|
|
74
|
+
dory/resilience/__init__.py,sha256=5SxTG4SGEMImLu4_PTjAk1ZY_kaisbkhq0g6speDdzU,945
|
|
75
|
+
dory/resilience/circuit_breaker.py,sha256=5NNoBBrPGVXB8RNUwtDa_kzRRNMUm6jXaw2GXpV1m0U,15018
|
|
76
|
+
dory/resilience/retry.py,sha256=OmFrMmdxe2moH_KX1FNCVNyZgSkpDGNgKmGjas8pqMM,12995
|
|
77
|
+
dory/utils/__init__.py,sha256=Lt6NKxAYCTm9iehLxhVk9elGv7l9poMS7smJE7bgF_E,672
|
|
78
|
+
dory/utils/errors.py,sha256=K3ofXRoTmGpXQ1WN836ut-WpvmT70obxRkNiYCk3EO0,1312
|
|
79
|
+
dory/utils/retry.py,sha256=WVrEEkSF2NW8RM1oRv3sKWSJkwVdh4BPXvu7jUPcPHo,3055
|
|
80
|
+
dory/utils/timeout.py,sha256=pDHpO1OfH9hfi8_-gUK2mEXTXb1outsTdAobO3jxbzs,2059
|
|
81
|
+
dory_processor_sdk-0.0.1.dist-info/licenses/LICENSE,sha256=jX3QGhMfb5WVZDxteBLGJG5Ib4g9a7VUDcmGocJFkqY,11355
|
|
82
|
+
dory_processor_sdk-0.0.1.dist-info/METADATA,sha256=OHXrmYQQ3yiUraKZdGE5HSvp8IkHXyMA_MgpWoVwHk4,13937
|
|
83
|
+
dory_processor_sdk-0.0.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
84
|
+
dory_processor_sdk-0.0.1.dist-info/entry_points.txt,sha256=JtdjTIlmVvGH9F2TIPwQGtdxZOyv8xkxJSF9KeDT5UU,44
|
|
85
|
+
dory_processor_sdk-0.0.1.dist-info/top_level.txt,sha256=lbgLxVkyGEP63BrBq53S9m0Mv7t-ZP-B0WHKTmOpOz0,5
|
|
86
|
+
dory_processor_sdk-0.0.1.dist-info/RECORD,,
|