dbos 0.22.0a10__py3-none-any.whl → 0.23.0__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.
Potentially problematic release.
This version of dbos might be problematic. Click here for more details.
- dbos/__main__.py +26 -0
- dbos/_app_db.py +29 -24
- dbos/_cloudutils/cloudutils.py +4 -2
- dbos/_cloudutils/databases.py +4 -0
- dbos/_conductor/conductor.py +213 -0
- dbos/_conductor/protocol.py +197 -0
- dbos/_context.py +3 -1
- dbos/_core.py +73 -26
- dbos/_croniter.py +2 -2
- dbos/_dbos.py +74 -16
- dbos/_dbos_config.py +45 -11
- dbos/_debug.py +45 -0
- dbos/_error.py +11 -0
- dbos/_logger.py +5 -6
- dbos/_migrations/versions/5c361fc04708_added_system_tables.py +1 -1
- dbos/_queue.py +5 -1
- dbos/_recovery.py +23 -24
- dbos/_schemas/system_database.py +1 -1
- dbos/_sys_db.py +212 -187
- dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +1 -1
- dbos/_tracer.py +4 -4
- dbos/_utils.py +6 -0
- dbos/_workflow_commands.py +76 -111
- dbos/cli/cli.py +63 -21
- {dbos-0.22.0a10.dist-info → dbos-0.23.0.dist-info}/METADATA +7 -3
- {dbos-0.22.0a10.dist-info → dbos-0.23.0.dist-info}/RECORD +29 -24
- {dbos-0.22.0a10.dist-info → dbos-0.23.0.dist-info}/WHEEL +0 -0
- {dbos-0.22.0a10.dist-info → dbos-0.23.0.dist-info}/entry_points.txt +0 -0
- {dbos-0.22.0a10.dist-info → dbos-0.23.0.dist-info}/licenses/LICENSE +0 -0
dbos/_tracer.py
CHANGED
|
@@ -7,6 +7,8 @@ from opentelemetry.sdk.trace import TracerProvider
|
|
|
7
7
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
|
|
8
8
|
from opentelemetry.trace import Span
|
|
9
9
|
|
|
10
|
+
from dbos._utils import GlobalParams
|
|
11
|
+
|
|
10
12
|
from ._dbos_config import ConfigFile
|
|
11
13
|
|
|
12
14
|
if TYPE_CHECKING:
|
|
@@ -17,8 +19,6 @@ class DBOSTracer:
|
|
|
17
19
|
|
|
18
20
|
def __init__(self) -> None:
|
|
19
21
|
self.app_id = os.environ.get("DBOS__APPID", None)
|
|
20
|
-
self.app_version = os.environ.get("DBOS__APPVERSION", None)
|
|
21
|
-
self.executor_id = os.environ.get("DBOS__VMID", "local")
|
|
22
22
|
self.provider: Optional[TracerProvider] = None
|
|
23
23
|
|
|
24
24
|
def config(self, config: ConfigFile) -> None:
|
|
@@ -51,8 +51,8 @@ class DBOSTracer:
|
|
|
51
51
|
context = trace.set_span_in_context(parent) if parent else None
|
|
52
52
|
span: Span = tracer.start_span(name=attributes["name"], context=context)
|
|
53
53
|
attributes["applicationID"] = self.app_id
|
|
54
|
-
attributes["applicationVersion"] =
|
|
55
|
-
attributes["executorID"] =
|
|
54
|
+
attributes["applicationVersion"] = GlobalParams.app_version
|
|
55
|
+
attributes["executorID"] = GlobalParams.executor_id
|
|
56
56
|
for k, v in attributes.items():
|
|
57
57
|
if k != "name" and v is not None and isinstance(v, (str, bool, int, float)):
|
|
58
58
|
span.set_attribute(k, v)
|
dbos/_utils.py
ADDED
dbos/_workflow_commands.py
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
from typing import List, Optional, cast
|
|
2
2
|
|
|
3
|
-
import typer
|
|
4
|
-
|
|
5
3
|
from . import _serialization
|
|
6
|
-
from ._dbos_config import ConfigFile
|
|
7
4
|
from ._sys_db import (
|
|
8
5
|
GetQueuedWorkflowsInput,
|
|
9
6
|
GetWorkflowsInput,
|
|
@@ -14,69 +11,67 @@ from ._sys_db import (
|
|
|
14
11
|
|
|
15
12
|
|
|
16
13
|
class WorkflowInformation:
|
|
17
|
-
|
|
14
|
+
workflow_id: str
|
|
18
15
|
status: WorkflowStatuses
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
workflow_name: str
|
|
17
|
+
workflow_class_name: Optional[str]
|
|
18
|
+
workflow_config_name: Optional[str]
|
|
19
|
+
authenticated_user: Optional[str]
|
|
20
|
+
assumed_role: Optional[str]
|
|
21
|
+
authenticated_roles: Optional[str] # JSON list of roles.
|
|
22
22
|
input: Optional[_serialization.WorkflowInputs] # JSON (jsonpickle)
|
|
23
23
|
output: Optional[str] = None # JSON (jsonpickle)
|
|
24
|
+
request: Optional[str] # JSON (jsonpickle)
|
|
24
25
|
error: Optional[str] = None # JSON (jsonpickle)
|
|
26
|
+
created_at: Optional[int] # Unix epoch timestamp in ms
|
|
27
|
+
updated_at: Optional[int] # Unix epoch timestamp in ms
|
|
28
|
+
queue_name: Optional[str]
|
|
25
29
|
executor_id: Optional[str]
|
|
26
30
|
app_version: Optional[str]
|
|
27
31
|
app_id: Optional[str]
|
|
28
|
-
request: Optional[str] # JSON (jsonpickle)
|
|
29
32
|
recovery_attempts: Optional[int]
|
|
30
|
-
authenticated_user: Optional[str]
|
|
31
|
-
assumed_role: Optional[str]
|
|
32
|
-
authenticated_roles: Optional[str] # JSON list of roles.
|
|
33
|
-
queue_name: Optional[str]
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
def list_workflows(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
sys_db: SystemDatabase,
|
|
37
|
+
*,
|
|
38
|
+
workflow_ids: Optional[List[str]] = None,
|
|
39
|
+
user: Optional[str] = None,
|
|
40
|
+
start_time: Optional[str] = None,
|
|
41
|
+
end_time: Optional[str] = None,
|
|
42
|
+
status: Optional[str] = None,
|
|
43
|
+
request: bool = False,
|
|
44
|
+
app_version: Optional[str] = None,
|
|
45
|
+
name: Optional[str] = None,
|
|
46
|
+
limit: Optional[int] = None,
|
|
47
|
+
offset: Optional[int] = None,
|
|
48
|
+
sort_desc: bool = False,
|
|
46
49
|
) -> List[WorkflowInformation]:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
input.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
infos.append(info)
|
|
68
|
-
|
|
69
|
-
return infos
|
|
70
|
-
except Exception as e:
|
|
71
|
-
typer.echo(f"Error listing workflows: {e}")
|
|
72
|
-
return []
|
|
73
|
-
finally:
|
|
74
|
-
if sys_db:
|
|
75
|
-
sys_db.destroy()
|
|
50
|
+
input = GetWorkflowsInput()
|
|
51
|
+
input.workflow_ids = workflow_ids
|
|
52
|
+
input.authenticated_user = user
|
|
53
|
+
input.start_time = start_time
|
|
54
|
+
input.end_time = end_time
|
|
55
|
+
if status is not None:
|
|
56
|
+
input.status = cast(WorkflowStatuses, status)
|
|
57
|
+
input.application_version = app_version
|
|
58
|
+
input.limit = limit
|
|
59
|
+
input.name = name
|
|
60
|
+
input.offset = offset
|
|
61
|
+
input.sort_desc = sort_desc
|
|
62
|
+
|
|
63
|
+
output: GetWorkflowsOutput = sys_db.get_workflows(input)
|
|
64
|
+
infos: List[WorkflowInformation] = []
|
|
65
|
+
for workflow_id in output.workflow_uuids:
|
|
66
|
+
info = get_workflow(sys_db, workflow_id, request) # Call the method for each ID
|
|
67
|
+
if info is not None:
|
|
68
|
+
infos.append(info)
|
|
69
|
+
return infos
|
|
76
70
|
|
|
77
71
|
|
|
78
72
|
def list_queued_workflows(
|
|
79
|
-
|
|
73
|
+
sys_db: SystemDatabase,
|
|
74
|
+
*,
|
|
80
75
|
limit: Optional[int] = None,
|
|
81
76
|
start_time: Optional[str] = None,
|
|
82
77
|
end_time: Optional[str] = None,
|
|
@@ -84,62 +79,29 @@ def list_queued_workflows(
|
|
|
84
79
|
status: Optional[str] = None,
|
|
85
80
|
name: Optional[str] = None,
|
|
86
81
|
request: bool = False,
|
|
82
|
+
offset: Optional[int] = None,
|
|
83
|
+
sort_desc: bool = False,
|
|
87
84
|
) -> List[WorkflowInformation]:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
infos.append(info)
|
|
106
|
-
return infos
|
|
107
|
-
except Exception as e:
|
|
108
|
-
typer.echo(f"Error listing workflows: {e}")
|
|
109
|
-
return []
|
|
110
|
-
finally:
|
|
111
|
-
if sys_db:
|
|
112
|
-
sys_db.destroy()
|
|
85
|
+
input: GetQueuedWorkflowsInput = {
|
|
86
|
+
"queue_name": queue_name,
|
|
87
|
+
"start_time": start_time,
|
|
88
|
+
"end_time": end_time,
|
|
89
|
+
"status": status,
|
|
90
|
+
"limit": limit,
|
|
91
|
+
"name": name,
|
|
92
|
+
"offset": offset,
|
|
93
|
+
"sort_desc": sort_desc,
|
|
94
|
+
}
|
|
95
|
+
output: GetWorkflowsOutput = sys_db.get_queued_workflows(input)
|
|
96
|
+
infos: List[WorkflowInformation] = []
|
|
97
|
+
for workflow_id in output.workflow_uuids:
|
|
98
|
+
info = get_workflow(sys_db, workflow_id, request) # Call the method for each ID
|
|
99
|
+
if info is not None:
|
|
100
|
+
infos.append(info)
|
|
101
|
+
return infos
|
|
113
102
|
|
|
114
103
|
|
|
115
104
|
def get_workflow(
|
|
116
|
-
config: ConfigFile, uuid: str, request: bool
|
|
117
|
-
) -> Optional[WorkflowInformation]:
|
|
118
|
-
try:
|
|
119
|
-
sys_db = SystemDatabase(config)
|
|
120
|
-
info = _get_workflow_info(sys_db, uuid, request)
|
|
121
|
-
return info
|
|
122
|
-
except Exception as e:
|
|
123
|
-
typer.echo(f"Error getting workflow: {e}")
|
|
124
|
-
return None
|
|
125
|
-
finally:
|
|
126
|
-
if sys_db:
|
|
127
|
-
sys_db.destroy()
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def cancel_workflow(config: ConfigFile, uuid: str) -> None:
|
|
131
|
-
try:
|
|
132
|
-
sys_db = SystemDatabase(config)
|
|
133
|
-
sys_db.cancel_workflow(uuid)
|
|
134
|
-
except Exception as e:
|
|
135
|
-
typer.echo(f"Failed to connect to DBOS system database: {e}")
|
|
136
|
-
raise e
|
|
137
|
-
finally:
|
|
138
|
-
if sys_db:
|
|
139
|
-
sys_db.destroy()
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def _get_workflow_info(
|
|
143
105
|
sys_db: SystemDatabase, workflowUUID: str, getRequest: bool
|
|
144
106
|
) -> Optional[WorkflowInformation]:
|
|
145
107
|
|
|
@@ -149,19 +111,22 @@ def _get_workflow_info(
|
|
|
149
111
|
|
|
150
112
|
winfo = WorkflowInformation()
|
|
151
113
|
|
|
152
|
-
winfo.
|
|
114
|
+
winfo.workflow_id = workflowUUID
|
|
153
115
|
winfo.status = info["status"]
|
|
154
|
-
winfo.
|
|
155
|
-
winfo.
|
|
156
|
-
winfo.
|
|
157
|
-
winfo.executor_id = info["executor_id"]
|
|
158
|
-
winfo.app_version = info["app_version"]
|
|
159
|
-
winfo.app_id = info["app_id"]
|
|
160
|
-
winfo.recovery_attempts = info["recovery_attempts"]
|
|
116
|
+
winfo.workflow_name = info["name"]
|
|
117
|
+
winfo.workflow_class_name = info["class_name"]
|
|
118
|
+
winfo.workflow_config_name = info["config_name"]
|
|
161
119
|
winfo.authenticated_user = info["authenticated_user"]
|
|
162
120
|
winfo.assumed_role = info["assumed_role"]
|
|
163
121
|
winfo.authenticated_roles = info["authenticated_roles"]
|
|
122
|
+
winfo.request = info["request"]
|
|
123
|
+
winfo.created_at = info["created_at"]
|
|
124
|
+
winfo.updated_at = info["updated_at"]
|
|
164
125
|
winfo.queue_name = info["queue_name"]
|
|
126
|
+
winfo.executor_id = info["executor_id"]
|
|
127
|
+
winfo.app_version = info["app_version"]
|
|
128
|
+
winfo.app_id = info["app_id"]
|
|
129
|
+
winfo.recovery_attempts = info["recovery_attempts"]
|
|
165
130
|
|
|
166
131
|
# no input field
|
|
167
132
|
input_data = sys_db.get_workflow_inputs(workflowUUID)
|
dbos/cli/cli.py
CHANGED
|
@@ -15,16 +15,13 @@ from rich import print
|
|
|
15
15
|
from rich.prompt import IntPrompt
|
|
16
16
|
from typing_extensions import Annotated
|
|
17
17
|
|
|
18
|
+
from dbos._debug import debug_workflow, parse_start_command
|
|
19
|
+
|
|
18
20
|
from .. import load_config
|
|
19
21
|
from .._app_db import ApplicationDatabase
|
|
20
22
|
from .._dbos_config import _is_valid_app_name
|
|
21
23
|
from .._sys_db import SystemDatabase, reset_system_database
|
|
22
|
-
from .._workflow_commands import
|
|
23
|
-
cancel_workflow,
|
|
24
|
-
get_workflow,
|
|
25
|
-
list_queued_workflows,
|
|
26
|
-
list_workflows,
|
|
27
|
-
)
|
|
24
|
+
from .._workflow_commands import get_workflow, list_queued_workflows, list_workflows
|
|
28
25
|
from ..cli._github_init import create_template_from_github
|
|
29
26
|
from ._template_init import copy_template, get_project_name, get_templates_directory
|
|
30
27
|
|
|
@@ -78,7 +75,10 @@ def start() -> None:
|
|
|
78
75
|
|
|
79
76
|
# If the child is still running, force kill it
|
|
80
77
|
if process.poll() is None:
|
|
81
|
-
|
|
78
|
+
try:
|
|
79
|
+
os.killpg(os.getpgid(process.pid), signal.SIGKILL)
|
|
80
|
+
except Exception:
|
|
81
|
+
pass
|
|
82
82
|
|
|
83
83
|
# Exit immediately
|
|
84
84
|
os._exit(process.returncode if process.returncode is not None else 1)
|
|
@@ -237,6 +237,22 @@ def reset(
|
|
|
237
237
|
return
|
|
238
238
|
|
|
239
239
|
|
|
240
|
+
@app.command(help="Replay Debug a DBOS workflow")
|
|
241
|
+
def debug(
|
|
242
|
+
workflow_id: Annotated[str, typer.Argument(help="Workflow ID to debug")],
|
|
243
|
+
) -> None:
|
|
244
|
+
config = load_config(silent=True, use_db_wizard=False)
|
|
245
|
+
start = config["runtimeConfig"]["start"]
|
|
246
|
+
if not start:
|
|
247
|
+
typer.echo("No start commands found in 'dbos-config.yaml'")
|
|
248
|
+
raise typer.Exit(code=1)
|
|
249
|
+
if len(start) > 1:
|
|
250
|
+
typer.echo("Multiple start commands found in 'dbos-config.yaml'")
|
|
251
|
+
raise typer.Exit(code=1)
|
|
252
|
+
entrypoint = parse_start_command(start[0])
|
|
253
|
+
debug_workflow(workflow_id, entrypoint)
|
|
254
|
+
|
|
255
|
+
|
|
240
256
|
@workflow.command(help="List workflows for your application")
|
|
241
257
|
def list(
|
|
242
258
|
limit: Annotated[
|
|
@@ -290,25 +306,37 @@ def list(
|
|
|
290
306
|
request: Annotated[
|
|
291
307
|
bool,
|
|
292
308
|
typer.Option("--request", help="Retrieve workflow request information"),
|
|
293
|
-
] =
|
|
309
|
+
] = False,
|
|
294
310
|
) -> None:
|
|
295
311
|
config = load_config(silent=True)
|
|
312
|
+
sys_db = SystemDatabase(config)
|
|
296
313
|
workflows = list_workflows(
|
|
297
|
-
|
|
314
|
+
sys_db,
|
|
315
|
+
limit=limit,
|
|
316
|
+
user=user,
|
|
317
|
+
start_time=starttime,
|
|
318
|
+
end_time=endtime,
|
|
319
|
+
status=status,
|
|
320
|
+
request=request,
|
|
321
|
+
app_version=appversion,
|
|
322
|
+
name=name,
|
|
298
323
|
)
|
|
299
324
|
print(jsonpickle.encode(workflows, unpicklable=False))
|
|
300
325
|
|
|
301
326
|
|
|
302
327
|
@workflow.command(help="Retrieve the status of a workflow")
|
|
303
328
|
def get(
|
|
304
|
-
|
|
329
|
+
workflow_id: Annotated[str, typer.Argument()],
|
|
305
330
|
request: Annotated[
|
|
306
331
|
bool,
|
|
307
332
|
typer.Option("--request", help="Retrieve workflow request information"),
|
|
308
|
-
] =
|
|
333
|
+
] = False,
|
|
309
334
|
) -> None:
|
|
310
335
|
config = load_config(silent=True)
|
|
311
|
-
|
|
336
|
+
sys_db = SystemDatabase(config)
|
|
337
|
+
print(
|
|
338
|
+
jsonpickle.encode(get_workflow(sys_db, workflow_id, request), unpicklable=False)
|
|
339
|
+
)
|
|
312
340
|
|
|
313
341
|
|
|
314
342
|
@workflow.command(
|
|
@@ -316,10 +344,23 @@ def get(
|
|
|
316
344
|
)
|
|
317
345
|
def cancel(
|
|
318
346
|
uuid: Annotated[str, typer.Argument()],
|
|
347
|
+
host: Annotated[
|
|
348
|
+
typing.Optional[str],
|
|
349
|
+
typer.Option("--host", "-H", help="Specify the admin host"),
|
|
350
|
+
] = "localhost",
|
|
351
|
+
port: Annotated[
|
|
352
|
+
typing.Optional[int],
|
|
353
|
+
typer.Option("--port", "-p", help="Specify the admin port"),
|
|
354
|
+
] = 3001,
|
|
319
355
|
) -> None:
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
356
|
+
response = requests.post(
|
|
357
|
+
f"http://{host}:{port}/workflows/{uuid}/cancel", json=[], timeout=5
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
if response.status_code == 204:
|
|
361
|
+
print(f"Workflow {uuid} has been cancelled")
|
|
362
|
+
else:
|
|
363
|
+
print(f"Failed to cancel workflow {uuid}. Status code: {response.status_code}")
|
|
323
364
|
|
|
324
365
|
|
|
325
366
|
@workflow.command(help="Resume a workflow that has been cancelled")
|
|
@@ -327,7 +368,7 @@ def resume(
|
|
|
327
368
|
uuid: Annotated[str, typer.Argument()],
|
|
328
369
|
host: Annotated[
|
|
329
370
|
typing.Optional[str],
|
|
330
|
-
typer.Option("--host", "-
|
|
371
|
+
typer.Option("--host", "-H", help="Specify the admin host"),
|
|
331
372
|
] = "localhost",
|
|
332
373
|
port: Annotated[
|
|
333
374
|
typing.Optional[int],
|
|
@@ -338,7 +379,7 @@ def resume(
|
|
|
338
379
|
f"http://{host}:{port}/workflows/{uuid}/resume", json=[], timeout=5
|
|
339
380
|
)
|
|
340
381
|
|
|
341
|
-
if response.status_code ==
|
|
382
|
+
if response.status_code == 204:
|
|
342
383
|
print(f"Workflow {uuid} has been resumed")
|
|
343
384
|
else:
|
|
344
385
|
print(f"Failed to resume workflow {uuid}. Status code: {response.status_code}")
|
|
@@ -349,7 +390,7 @@ def restart(
|
|
|
349
390
|
uuid: Annotated[str, typer.Argument()],
|
|
350
391
|
host: Annotated[
|
|
351
392
|
typing.Optional[str],
|
|
352
|
-
typer.Option("--host", "-
|
|
393
|
+
typer.Option("--host", "-H", help="Specify the admin host"),
|
|
353
394
|
] = "localhost",
|
|
354
395
|
port: Annotated[
|
|
355
396
|
typing.Optional[int],
|
|
@@ -360,7 +401,7 @@ def restart(
|
|
|
360
401
|
f"http://{host}:{port}/workflows/{uuid}/restart", json=[], timeout=5
|
|
361
402
|
)
|
|
362
403
|
|
|
363
|
-
if response.status_code ==
|
|
404
|
+
if response.status_code == 204:
|
|
364
405
|
print(f"Workflow {uuid} has been restarted")
|
|
365
406
|
else:
|
|
366
407
|
print(f"Failed to resume workflow {uuid}. Status code: {response.status_code}")
|
|
@@ -415,11 +456,12 @@ def list_queue(
|
|
|
415
456
|
request: Annotated[
|
|
416
457
|
bool,
|
|
417
458
|
typer.Option("--request", help="Retrieve workflow request information"),
|
|
418
|
-
] =
|
|
459
|
+
] = False,
|
|
419
460
|
) -> None:
|
|
420
461
|
config = load_config(silent=True)
|
|
462
|
+
sys_db = SystemDatabase(config)
|
|
421
463
|
workflows = list_queued_workflows(
|
|
422
|
-
|
|
464
|
+
sys_db=sys_db,
|
|
423
465
|
limit=limit,
|
|
424
466
|
start_time=start_time,
|
|
425
467
|
end_time=end_time,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dbos
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.23.0
|
|
4
4
|
Summary: Ultra-lightweight durable execution in Python
|
|
5
5
|
Author-Email: "DBOS, Inc." <contact@dbos.dev>
|
|
6
6
|
License: MIT
|
|
@@ -23,6 +23,7 @@ Requires-Dist: docker>=7.1.0
|
|
|
23
23
|
Requires-Dist: cryptography>=43.0.3
|
|
24
24
|
Requires-Dist: rich>=13.9.4
|
|
25
25
|
Requires-Dist: pyjwt>=2.10.1
|
|
26
|
+
Requires-Dist: websockets>=15.0
|
|
26
27
|
Description-Content-Type: text/markdown
|
|
27
28
|
|
|
28
29
|
|
|
@@ -78,6 +79,9 @@ You can use DBOS to add reliable background jobs or cron scheduling or queues to
|
|
|
78
79
|
Install and configure with:
|
|
79
80
|
|
|
80
81
|
```shell
|
|
82
|
+
python3 -m venv dbos-example/.venv
|
|
83
|
+
cd dbos-example
|
|
84
|
+
source .venv/bin/activate
|
|
81
85
|
pip install dbos
|
|
82
86
|
dbos init --config
|
|
83
87
|
```
|
|
@@ -103,7 +107,7 @@ def step_two():
|
|
|
103
107
|
def dbos_workflow():
|
|
104
108
|
step_one()
|
|
105
109
|
for _ in range(5):
|
|
106
|
-
print("Press Control +
|
|
110
|
+
print("Press Control + C twice to stop the app...")
|
|
107
111
|
DBOS.sleep(1)
|
|
108
112
|
step_two()
|
|
109
113
|
|
|
@@ -114,7 +118,7 @@ def fastapi_endpoint():
|
|
|
114
118
|
|
|
115
119
|
Save the program into `main.py` and start it with `fastapi run`.
|
|
116
120
|
Visit `localhost:8000` in your browser to start the workflow.
|
|
117
|
-
When prompted, press `Control +
|
|
121
|
+
When prompted, press `Control + C` (You may need to press `Control + C` twice quickly, or press `Control + \`, if `Control + C` is not effective in your environment) to force quit your application.
|
|
118
122
|
It should crash midway through the workflow, having completed step one but not step two.
|
|
119
123
|
Then, restart your app with `fastapi run`.
|
|
120
124
|
It should resume the workflow from where it left off, completing step two without re-executing step one.
|
|
@@ -1,47 +1,51 @@
|
|
|
1
|
-
dbos-0.
|
|
2
|
-
dbos-0.
|
|
3
|
-
dbos-0.
|
|
4
|
-
dbos-0.
|
|
1
|
+
dbos-0.23.0.dist-info/METADATA,sha256=t4gZ7mV0a8DV7bAp4QHmcxGKZpskI9NarxKhQpM03qY,5553
|
|
2
|
+
dbos-0.23.0.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
|
3
|
+
dbos-0.23.0.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
|
|
4
|
+
dbos-0.23.0.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
|
|
5
5
|
dbos/__init__.py,sha256=CxRHBHEthPL4PZoLbZhp3rdm44-KkRTT2-7DkK9d4QQ,724
|
|
6
|
+
dbos/__main__.py,sha256=P7jAr-7L9XE5mrsQ7i4b-bLr2ap1tCQfhMByLCRWDj0,568
|
|
6
7
|
dbos/_admin_server.py,sha256=YiVn5lywz2Vg8_juyNHOYl0HVEy48--7b4phwK7r92o,5732
|
|
7
|
-
dbos/_app_db.py,sha256=
|
|
8
|
+
dbos/_app_db.py,sha256=QFL1ceCugJFj_LBvK_G_0tt5jjyTM-4KnqmhbuC1ggg,5826
|
|
8
9
|
dbos/_classproperty.py,sha256=f0X-_BySzn3yFDRKB2JpCbLYQ9tLwt1XftfshvY7CBs,626
|
|
9
10
|
dbos/_cloudutils/authentication.py,sha256=V0fCWQN9stCkhbuuxgPTGpvuQcDqfU3KAxPAh01vKW4,5007
|
|
10
|
-
dbos/_cloudutils/cloudutils.py,sha256=
|
|
11
|
-
dbos/_cloudutils/databases.py,sha256=
|
|
12
|
-
dbos/
|
|
13
|
-
dbos/
|
|
14
|
-
dbos/
|
|
11
|
+
dbos/_cloudutils/cloudutils.py,sha256=YC7jGsIopT0KveLsqbRpQk2KlRBk-nIRC_UCgep4f3o,7797
|
|
12
|
+
dbos/_cloudutils/databases.py,sha256=_shqaqSvhY4n2ScgQ8IP5PDZvzvcx3YBKV8fj-cxhSY,8543
|
|
13
|
+
dbos/_conductor/conductor.py,sha256=uX6DsAw0vpu4Gf5pxZJL7YrMSfJptCvRCnt14AUURFo,11123
|
|
14
|
+
dbos/_conductor/protocol.py,sha256=HLHS1bOdFdQhwA05wXDCd3O704ki72ju1UUBXz0UB-0,4841
|
|
15
|
+
dbos/_context.py,sha256=Ue5qu3rzLfRmPkz-UUZi9ZS8iXpapRN0NTM4mbA2QmQ,17738
|
|
16
|
+
dbos/_core.py,sha256=UQb068FT59Op-F5RmtxreSeSQ1_wljOso0dQCUOPrC4,37528
|
|
17
|
+
dbos/_croniter.py,sha256=XHAyUyibs_59sJQfSNWkP7rqQY6_XrlfuuCxk4jYqek,47559
|
|
15
18
|
dbos/_db_wizard.py,sha256=6tfJaCRa1NtkUdNW75a2yvi_mEgnPJ9C1HP2zPG1hCU,8067
|
|
16
|
-
dbos/_dbos.py,sha256=
|
|
17
|
-
dbos/_dbos_config.py,sha256=
|
|
18
|
-
dbos/
|
|
19
|
+
dbos/_dbos.py,sha256=0kX3fgdTqAn-eMKSbh73LFVR08YPMoB030g4dzvc9Yk,41150
|
|
20
|
+
dbos/_dbos_config.py,sha256=_VETbEsMZ66563A8sX05B_coKz2BrILbIm9H5BmnPmk,9572
|
|
21
|
+
dbos/_debug.py,sha256=wcvjM2k4BrK7mlYjImUZXNBUB00fPGjQrNimZXlj76c,1491
|
|
22
|
+
dbos/_error.py,sha256=xqB7b7g5AF_OwOvqLKLXL1xldn2gAtORix2ZC2B8zK0,5089
|
|
19
23
|
dbos/_fastapi.py,sha256=ke03vqsSYDnO6XeOtOVFXj0-f-v1MGsOxa9McaROvNc,3616
|
|
20
24
|
dbos/_flask.py,sha256=DZKUZR5-xOzPI7tYZ53r2PvvHVoAb8SYwLzMVFsVfjI,2608
|
|
21
25
|
dbos/_kafka.py,sha256=o6DbwnsYRDtvVTZVsN7BAK8cdP79AfoWX3Q7CGY2Yuo,4199
|
|
22
26
|
dbos/_kafka_message.py,sha256=NYvOXNG3Qn7bghn1pv3fg4Pbs86ILZGcK4IB-MLUNu0,409
|
|
23
|
-
dbos/_logger.py,sha256=
|
|
27
|
+
dbos/_logger.py,sha256=utroNAXW71MLYb5D3lsM5xifXT19n5mAidyW-4kwyMA,3499
|
|
24
28
|
dbos/_migrations/env.py,sha256=38SIGVbmn_VV2x2u1aHLcPOoWgZ84eCymf3g_NljmbU,1626
|
|
25
29
|
dbos/_migrations/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
|
|
26
30
|
dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py,sha256=ICLPl8CN9tQXMsLDsAj8z1TsL831-Z3F8jSBvrR-wyw,736
|
|
27
31
|
dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py,sha256=ZBYrtTdxy64HxIAlOes89fVIk2P1gNaJack7wuC_epg,873
|
|
28
|
-
dbos/_migrations/versions/5c361fc04708_added_system_tables.py,sha256=
|
|
32
|
+
dbos/_migrations/versions/5c361fc04708_added_system_tables.py,sha256=Xr9hBDJjkAtymlauOmAy00yUHj0VVUaEz7kNwEM9IwE,6403
|
|
29
33
|
dbos/_migrations/versions/a3b18ad34abe_added_triggers.py,sha256=Rv0ZsZYZ_WdgGEULYsPfnp4YzaO5L198gDTgYY39AVA,2022
|
|
30
34
|
dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py,sha256=8PyFi8rd6CN-mUro43wGhsg5wcQWKZPRHD6jw8R5pVc,986
|
|
31
35
|
dbos/_migrations/versions/d76646551a6c_workflow_queue.py,sha256=G942nophZ2uC2vc4hGBC02Ptng1715roTjY3xiyzZU4,729
|
|
32
36
|
dbos/_migrations/versions/eab0cc1d9a14_job_queue.py,sha256=uvhFOtqbBreCePhAxZfIT0qCAI7BiZTou9wt6QnbY7c,1412
|
|
33
37
|
dbos/_outcome.py,sha256=FDMgWVjZ06vm9xO-38H17mTqBImUYQxgKs_bDCSIAhE,6648
|
|
34
|
-
dbos/_queue.py,sha256=
|
|
35
|
-
dbos/_recovery.py,sha256=
|
|
38
|
+
dbos/_queue.py,sha256=I2gBc7zQ4G0vyDDBnKwIFzxtqfD7DxHO2IZ41brFSOM,2927
|
|
39
|
+
dbos/_recovery.py,sha256=4KyZb0XJEUGH7ekYT1kpx38i6y5vygPeH75Ta7RZjYo,2596
|
|
36
40
|
dbos/_registrations.py,sha256=_zy6k944Ll8QwqU12Kr3OP23ukVtm8axPNN1TS_kJRc,6717
|
|
37
41
|
dbos/_request.py,sha256=cX1B3Atlh160phgS35gF1VEEV4pD126c9F3BDgBmxZU,929
|
|
38
42
|
dbos/_roles.py,sha256=iOsgmIAf1XVzxs3gYWdGRe1B880YfOw5fpU7Jwx8_A8,2271
|
|
39
43
|
dbos/_scheduler.py,sha256=0I3e8Y-OIBG3wiUCIskShd-Sk_eUFCFyRB5u4L7IHXI,1940
|
|
40
44
|
dbos/_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
45
|
dbos/_schemas/application_database.py,sha256=KeyoPrF7hy_ODXV7QNike_VFSD74QBRfQ76D7QyE9HI,966
|
|
42
|
-
dbos/_schemas/system_database.py,sha256=
|
|
46
|
+
dbos/_schemas/system_database.py,sha256=16146P4TLjAGjTYykOs_KUd2c_geJ5fuhk0ko85C65M,5211
|
|
43
47
|
dbos/_serialization.py,sha256=YCYv0qKAwAZ1djZisBC7khvKqG-5OcIv9t9EC5PFIog,1743
|
|
44
|
-
dbos/_sys_db.py,sha256=
|
|
48
|
+
dbos/_sys_db.py,sha256=ul17Pe_qmslBMU1UZpY_UpCGnvUhcAxLAbSBps0YB20,64587
|
|
45
49
|
dbos/_templates/dbos-db-starter/README.md,sha256=GhxhBj42wjTt1fWEtwNriHbJuKb66Vzu89G4pxNHw2g,930
|
|
46
50
|
dbos/_templates/dbos-db-starter/__package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
47
51
|
dbos/_templates/dbos-db-starter/__package/main.py,sha256=eI0SS9Nwj-fldtiuSzIlIG6dC91GXXwdRsoHxv6S_WI,2719
|
|
@@ -50,14 +54,15 @@ dbos/_templates/dbos-db-starter/alembic.ini,sha256=VKBn4Gy8mMuCdY7Hip1jmo3wEUJ1V
|
|
|
50
54
|
dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos,sha256=OMlcpdYUJKjyAme7phOz3pbn9upcIRjm42iwEThWUEQ,495
|
|
51
55
|
dbos/_templates/dbos-db-starter/migrations/env.py.dbos,sha256=GUV6sjkDzf9Vl6wkGEd0RSkK-ftRfV6EUwSQdd0qFXg,2392
|
|
52
56
|
dbos/_templates/dbos-db-starter/migrations/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
|
|
53
|
-
dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py,sha256=
|
|
57
|
+
dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py,sha256=MpS7LGaJS0CpvsjhfDkp9EJqvMvVCjRPfUp4c0aE2ys,941
|
|
54
58
|
dbos/_templates/dbos-db-starter/start_postgres_docker.py,sha256=lQVLlYO5YkhGPEgPqwGc7Y8uDKse9HsWv5fynJEFJHM,1681
|
|
55
|
-
dbos/_tracer.py,sha256=
|
|
56
|
-
dbos/
|
|
59
|
+
dbos/_tracer.py,sha256=_Id9j9kCrptSNpEpLiRk_g5VPp-DrTWP1WNZInd5BA4,2439
|
|
60
|
+
dbos/_utils.py,sha256=wjOJzxN66IzL9p4dwcEmQACRQah_V09G6mJI2exQfOM,155
|
|
61
|
+
dbos/_workflow_commands.py,sha256=CEzR5XghoZscbc2RHb9G-7Eoo4MMuzfeTo-QBZu4VPY,4690
|
|
57
62
|
dbos/cli/_github_init.py,sha256=Y_bDF9gfO2jB1id4FV5h1oIxEJRWyqVjhb7bNEa5nQ0,3224
|
|
58
63
|
dbos/cli/_template_init.py,sha256=AfuMaO8bmr9WsPNHr6j2cp7kjVVZDUpH7KpbTg0hhFs,2722
|
|
59
|
-
dbos/cli/cli.py,sha256=
|
|
64
|
+
dbos/cli/cli.py,sha256=ThomRytw7EP5iOcrjEgwnpaWgXNTLfnFEBBvCGHxtJs,15590
|
|
60
65
|
dbos/dbos-config.schema.json,sha256=X5TpXNcARGceX0zQs0fVgtZW_Xj9uBbY5afPt9Rz9yk,5741
|
|
61
66
|
dbos/py.typed,sha256=QfzXT1Ktfk3Rj84akygc7_42z0lRpCq0Ilh8OXI6Zas,44
|
|
62
67
|
version/__init__.py,sha256=L4sNxecRuqdtSFdpUGX3TtBi9KL3k7YsZVIvv-fv9-A,1678
|
|
63
|
-
dbos-0.
|
|
68
|
+
dbos-0.23.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|