Commit 93f4205
authored
Toward executor objects (#44)
This PR:
- Encapsulate the state of a "DBOS executor" in an interface exported by
the package. This must be typeless, hence:
- Preserves compile-time type checking by exposing package-level methods
that accept a DBOS executor interface
- Attempts to keep the programming interface reasonable by creating a
`DBOSContext` type which holds both DBOS executor functionalities and
extends the native `context.Context` interface.
The mirror functions are made with the same signature so it is more
intuitive for users when mocking.
Few things to note/improve/think about
- Workflow functions now require our `DBOSContext` instead of a
`context.Context`. This is because all operations need a context. Steps
on the other hand can accept a normal context.Context if they wish to,
and upcast it to `DBOSContext` if they need to do DBOS stuff.
- DBOSContext is local to a process. This means the user cannot expect a
context to: 1) be passed to the workflow at recovery 2) be passed to a
queued task by the queue runner. This is quite un-intuitive w.r.t native
Golang `context.Context`
- SetEvent / Send types are not greatly named
- We should find a way for library specific contexts to play together.
Right now a gin handler looks like: `func checkoutEndpoint(c
*gin.Context, dbosCtx dbos.DBOSContext, logger *logrus.Logger) {`
Another thing this PR does, on the path of improving the step interface,
is allowing users to pass step parameters through the context, using the
`WithValue` method. We'll have to improve on this as well: right now
`WithValue` does nothing if the provided interface is not a concrete
`dbosContext` (our internal struct implementing `DBOSContext`), so a
user cannot rely on this in their tests if they mock `DBOSContext`.
Immediate next PRs:
- Move logger, queue registry in DBOSContext
- Tighten usage of context to manage resources like queue runner
- Add tests running workflows inside goroutines
- Improve step UX
`DBOSContext` exposes all the DBOS methods an end-user is expected to
write in their code:
```golang
type DBOSContext interface {
context.Context
// Context Lifecycle
Launch() error
Shutdown()
// Workflow operations
RunAsStep(_ DBOSContext, fn StepFunc, input ...any) (any, error)
RunAsWorkflow(_ DBOSContext, fn WorkflowFunc, input any, opts ...WorkflowOption) (WorkflowHandle[any], error)
Send(_ DBOSContext, input WorkflowSendInputInternal) error
Recv(_ DBOSContext, input WorkflowRecvInput) (any, error)
SetEvent(_ DBOSContext, input WorkflowSetEventInput) error
GetEvent(_ DBOSContext, input WorkflowGetEventInput) (any, error)
Sleep(duration time.Duration) (time.Duration, error)
GetWorkflowID() (string, error)
// Workflow management
RetrieveWorkflow(_ DBOSContext, workflowID string) (WorkflowHandle[any], error)
// Accessors
GetApplicationVersion() string
GetExecutorID() string
GetApplicationID() string
}
```
New library usage:
```golang
dbosContext, err := dbos.NewDBOSContext(dbos.Config{
AppName: "widget_store_go",
DatabaseURL: os.Getenv("DBOS_SYSTEM_DATABASE_URL"),
})
if err != nil {
logger.WithError(err).Fatal("DBOS initialization failed")
}
dbos.RegisterWorkflow(dbosContext, checkoutWorkflow)
dbos.RegisterWorkflow(dbosContext, dispatchOrderWorkflow)
err = dbosContext.Launch()
if err != nil {
logger.WithError(err).Fatal("DBOS service start failed")
}
defer dbosContext.Shutdown()
```
Starting workflows:
```golang
handle, err := dbos.RunAsWorkflow(dbosCtx, checkoutWorkflow, "", dbos.WithWorkflowID(idempotencyKey))
```
The testing can be done as:
```golang
dbosContextMock := mocks.NewMockDBOSContext(t)
// Test running the wrapped workflow
t.Run("Payment fails", func(t *testing.T) {
wfID := "test-workflow-id"
// Set expectations on what DBOS stuff that happens within the workflow
dbosContextMock.On("GetWorkflowID").Return(wfID, nil)
dbosContextMock.On("RunAsStep", dbosContextMock, mock.Anything, mock.Anything).Return(1, nil).Once()
dbosContextMock.On("RunAsStep", dbosContextMock, mock.Anything, mock.Anything).Return(false, nil).Once()
dbosContextMock.On("RunAsStep", dbosContextMock, mock.Anything, mock.Anything).Return("", nil).Once()
dbosContextMock.On("SetEvent", dbosContextMock, mock.Anything).Return(nil).Once()
res, err := checkoutWorkflow(dbosContextMock, "")
if err != nil {
t.Fatalf("checkout workflow failed: %v", err)
}
if res != "" {
t.Fatalf("expected empty result, got %s", res)
}
dbosContextMock.AssertExpectations(t)
})
```1 parent 77b5acf commit 93f4205
File tree
13 files changed
+1260
-852
lines changed- dbos
13 files changed
+1260
-852
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
28 | | - | |
| 28 | + | |
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| |||
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
53 | | - | |
| 53 | + | |
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
14 | | - | |
| 14 | + | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
18 | | - | |
19 | 17 | | |
20 | | - | |
| 18 | + | |
21 | 19 | | |
22 | 20 | | |
23 | 21 | | |
24 | 22 | | |
25 | 23 | | |
26 | 24 | | |
27 | | - | |
| 25 | + | |
28 | 26 | | |
29 | 27 | | |
30 | 28 | | |
31 | 29 | | |
32 | 30 | | |
33 | 31 | | |
34 | | - | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | | - | |
| 48 | + | |
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
52 | | - | |
| 52 | + | |
| 53 | + | |
53 | 54 | | |
54 | 55 | | |
55 | 56 | | |
56 | 57 | | |
57 | 58 | | |
58 | | - | |
59 | | - | |
60 | 59 | | |
61 | | - | |
| 60 | + | |
62 | 61 | | |
63 | 62 | | |
64 | 63 | | |
65 | 64 | | |
66 | 65 | | |
67 | 66 | | |
68 | 67 | | |
69 | | - | |
| 68 | + | |
70 | 69 | | |
71 | 70 | | |
72 | 71 | | |
73 | 72 | | |
74 | 73 | | |
75 | 74 | | |
76 | | - | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
77 | 78 | | |
78 | 79 | | |
79 | 80 | | |
80 | 81 | | |
81 | 82 | | |
82 | 83 | | |
83 | | - | |
| 84 | + | |
84 | 85 | | |
85 | 86 | | |
86 | 87 | | |
87 | | - | |
| 88 | + | |
| 89 | + | |
88 | 90 | | |
89 | 91 | | |
90 | 92 | | |
| |||
0 commit comments