Skip to content

Commit e63f890

Browse files
authored
Readme and docs (#52)
- Make system DB private - Update README - Add docs for https://pkg.go.dev/github.com/dbos-inc/dbos-transact-go
1 parent 13a3aac commit e63f890

File tree

11 files changed

+695
-288
lines changed

11 files changed

+695
-288
lines changed

README.md

Lines changed: 182 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
<div align="center">
22

3-
# DBOS Transact: Lightweight Durable Workflows
3+
[![Go Reference](https://pkg.go.dev/badge/github.com/dbos-inc/dbos-transact-go.svg)](https://pkg.go.dev/github.com/dbos-inc/dbos-transact-go)
4+
[![Go Report Card](https://goreportcard.com/badge/github.com/dbos-inc/dbos-transact-go)](https://goreportcard.com/report/github.com/dbos-inc/dbos-transact-go)
5+
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/dbos-inc/dbos-transact-go?sort=semver)](https://github.com/dbos-inc/dbos-transact-go/releases)
6+
7+
8+
# DBOS Transact: Lightweight Durable Workflow Orchestration with Postgres
49

510
#### [Documentation](https://docs.dbos.dev/) &nbsp;&nbsp;&nbsp;&nbsp; [Examples](https://docs.dbos.dev/examples) &nbsp;&nbsp;&nbsp;&nbsp; [Github](https://github.com/dbos-inc) &nbsp;&nbsp;&nbsp;&nbsp; [Discord](https://discord.com/invite/jsmC6pXGgX)
611
</div>
712

8-
---
13+
#### This Golang version of DBOS Transact is in Alpha!
14+
For production ready Transacts, check our [Python](https://github.com/dbos-inc/dbos-transact-py) and [TypeScript](https://github.com/dbos-inc/dbos-transact-ts) versions.
915

10-
This package is in alpha -- the roadmap of missing features is listed [below](#roadmap).
16+
---
1117

1218
## What is DBOS?
1319

14-
DBOS provides lightweight durable workflows on top of Postgres. Instead of managing your own workflow orchestrator or task queue system, you can use DBOS to add durable workflows and queues to your program in just a few lines of code.
20+
DBOS provides lightweight durable workflow orchestration on top of Postgres. Instead of managing your own workflow orchestrator or task queue system, you can use DBOS to add durable workflows and queues to your program in just a few lines of code.
1521

1622

1723
## When Should I Use DBOS?
@@ -33,36 +39,67 @@ If your program ever fails, when it restarts all your workflows will automatical
3339
You add durable workflows to your existing Golang program by registering ordinary functions as workflows or running them as steps:
3440

3541
```golang
36-
var (
37-
wf = dbos.WithWorkflow(workflow)
42+
package main
43+
44+
import (
45+
"context"
46+
"fmt"
47+
"os"
48+
49+
"github.com/dbos-inc/dbos-transact-go/dbos"
3850
)
3951

40-
func workflow(ctx context.Context, _ string) (string, error) {
41-
_, err := dbos.RunAsStep(ctx, step1, "")
42-
if err != nil {
43-
return "", err
44-
}
45-
return dbos.RunAsStep(ctx, step2, "")
52+
func workflow(dbosCtx dbos.DBOSContext, _ string) (string, error) {
53+
_, err := dbos.RunAsStep(dbosCtx, func(ctx context.Context) (string, error) {
54+
return stepOne(ctx)
55+
})
56+
if err != nil {
57+
return "", err
58+
}
59+
return dbos.RunAsStep(dbosCtx, func(ctx context.Context) (string, error) {
60+
return stepTwo(ctx)
61+
})
4662
}
4763

48-
func step1(ctx context.Context, _ string) (string, error) {
49-
fmt.Println("Executing step 1")
50-
return "Step 1 completed", nil
64+
func stepOne(ctx context.Context) (string, error) {
65+
fmt.Println("Step one completed!")
66+
return "Step 1 completed", nil
5167
}
5268

53-
func step2(ctx context.Context, _ string) (string, error) {
54-
fmt.Println("Executing step 2")
55-
return "Step 2 completed - Workflow finished successfully", nil
69+
func stepTwo(ctx context.Context) (string, error) {
70+
fmt.Println("Step two completed!")
71+
return "Step 2 completed - Workflow finished successfully", nil
5672
}
57-
5873
func main() {
59-
err := dbos.Launch()
60-
if err != nil {
61-
panic(err)
62-
}
63-
defer dbos.Shutdown()
64-
65-
wf(context.Background(), "hello DBOS")
74+
// Initialize a DBOS context
75+
ctx, err := dbos.NewDBOSContext(dbos.Config{
76+
DatabaseURL: os.Getenv("DBOS_SYSTEM_DATABASE_URL"),
77+
AppName: "myapp",
78+
})
79+
if err != nil {
80+
panic(err)
81+
}
82+
83+
// Register a workflow
84+
dbos.RegisterWorkflow(ctx, workflow)
85+
86+
// Launch DBOS
87+
err = ctx.Launch()
88+
if err != nil {
89+
panic(err)
90+
}
91+
defer ctx.Cancel()
92+
93+
// Run a durable workflow and get its result
94+
handle, err := dbos.RunAsWorkflow(ctx, workflow, "")
95+
if err != nil {
96+
panic(err)
97+
}
98+
res, err := handle.GetResult()
99+
if err != nil {
100+
panic(err)
101+
}
102+
fmt.Println("Workflow result:", res)
66103
}
67104
```
68105

@@ -90,36 +127,56 @@ You can add queues to your workflows in just a couple lines of code.
90127
They don't require a separate queueing service or message broker&mdash;just Postgres.
91128

92129
```golang
93-
var (
94-
queue = dbos.NewWorkflowQueue("example-queue")
95-
taskWf = dbos.WithWorkflow(task)
130+
package main
131+
132+
import (
133+
"fmt"
134+
"os"
135+
"time"
136+
137+
"github.com/dbos-inc/dbos-transact-go/dbos"
96138
)
97139

98-
func task(ctx context.Context, i int) (int, error) {
99-
time.Sleep(5 * time.Second)
140+
func task(ctx dbos.DBOSContext, i int) (int, error) {
141+
ctx.Sleep(5 * time.Second)
100142
fmt.Printf("Task %d completed\n", i)
101143
return i, nil
102144
}
103145

104146
func main() {
105-
err := dbos.Launch()
147+
// Initialize a DBOS context
148+
ctx, err := dbos.NewDBOSContext(dbos.Config{
149+
DatabaseURL: os.Getenv("DBOS_SYSTEM_DATABASE_URL"),
150+
AppName: "myapp",
151+
})
106152
if err != nil {
107153
panic(err)
108154
}
109-
defer dbos.Shutdown()
110155

156+
// Register the workflow and create a durable queue
157+
dbos.RegisterWorkflow(ctx, task)
158+
queue := dbos.NewWorkflowQueue(ctx, "queue")
159+
160+
// Launch DBOS
161+
err = ctx.Launch()
162+
if err != nil {
163+
panic(err)
164+
}
165+
defer ctx.Cancel()
166+
167+
// Enqueue tasks and gather results
111168
fmt.Println("Enqueuing workflows")
112169
handles := make([]dbos.WorkflowHandle[int], 10)
113170
for i := range 10 {
114-
handle, err := taskWf(context.Background(), i, dbos.WithQueue(queue.Name))
171+
handle, err := dbos.RunAsWorkflow(ctx, task, i, dbos.WithQueue(queue.Name))
115172
if err != nil {
116173
panic(fmt.Sprintf("failed to enqueue step %d: %v", i, err))
117174
}
118175
handles[i] = handle
119176
}
120177
results := make([]int, 10)
121178
for i, handle := range handles {
122-
result, err := handle.GetResult(context.Background())
179+
result, err := handle.GetResult()
123180
if err != nil {
124181
panic(fmt.Sprintf("failed to get result for step %d: %v", i, err))
125182
}
@@ -130,47 +187,115 @@ func main() {
130187
```
131188
</details>
132189

190+
<details><summary><strong>🎫 Exactly-Once Event Processing</strong></summary>
191+
192+
####
193+
194+
Use DBOS to build reliable webhooks, event listeners, or Kafka consumers by starting a workflow exactly-once in response to an event.
195+
Acknowledge the event immediately while reliably processing it in the background.
196+
197+
For example:
198+
199+
```golang
200+
_, err := dbos.RunAsWorkflow(ctx, task, i, dbos.WithWorkflowID(exactlyOnceEventID))
201+
```
202+
</details>
203+
204+
<details><summary><strong>📅 Durable Scheduling</strong></summary>
205+
206+
####
207+
208+
Schedule workflows using cron syntax, or use durable sleep to pause workflows for as long as you like (even days or weeks) before executing.
209+
210+
```golang
211+
dbos.RegisterWorkflow(dbosCtx, func(ctx dbos.DBOSContext, scheduledTime time.Time) (string, error) {
212+
return fmt.Sprintf("Workflow executed at %s", scheduledTime), nil
213+
}, dbos.WithSchedule("* * * * * *")) // Every second
214+
```
215+
216+
You can add a durable sleep to any workflow with a single line of code.
217+
It stores its wakeup time in Postgres so the workflow sleeps through any interruption or restart, then always resumes on schedule.
218+
219+
```golang
220+
func workflow(ctx dbos.DBOSContext, duration time.Duration) (string, error) {
221+
ctx.Sleep(duration)
222+
return fmt.Sprintf("Workflow slept for %s", duration), nil
223+
}
224+
225+
handle, err := dbos.RunAsWorkflow(dbosCtx, workflow, time.Second*5)
226+
_, err = handle.GetResult()
227+
```
228+
229+
</details>
230+
231+
<details><summary><strong>📫 Durable Notifications</strong></summary>
232+
233+
####
234+
235+
Pause your workflow executions until a notification is received, or emit events from your workflow to send progress updates to external clients.
236+
All notifications are stored in Postgres, so they can be sent and received with exactly-once semantics.
237+
Set durable timeouts when waiting for events, so you can wait for as long as you like (even days or weeks) through interruptions or restarts, then resume once a notification arrives or the timeout is reached.
238+
239+
For example, build a reliable billing workflow that durably waits for a notification from a payments service, processing it exactly-once:
240+
241+
```golang
242+
func sendWorkflow(ctx dbos.DBOSContext, message string) (string, error) {
243+
err := dbos.Send(ctx, dbos.WorkflowSendInput[string]{
244+
DestinationID: "receiverID",
245+
Topic: "topic",
246+
Message: message,
247+
})
248+
}
249+
250+
func receiveWorkflow(ctx dbos.DBOSContext, topic string) (string, error) {
251+
return dbos.Recv[string](ctx, dbos.WorkflowRecvInput{Topic: topic, Timeout: 48 * time.Hour})
252+
}
253+
254+
// Start a receiver in the background
255+
recvHandle, err := dbos.RunAsWorkflow(dbosCtx, receiveWorkflow, "topic", dbos.WithWorkflowID("receiverID"))
256+
257+
// Send a message
258+
sendHandle, err := dbos.RunAsWorkflow(dbosCtx, sendWorkflow, "hola!")
259+
_, err = sendHandle.GetResult()
260+
261+
// Eventually get the response
262+
recvResult, err := recvHandle.GetResult()
263+
```
264+
265+
</details>
266+
267+
133268
## DBOS workflows
134269

135270
A workflow can be any function with the following signature:
136271
```golang
137-
type WorkflowFunc[P any, R any] func(ctx context.Context, input P) (R, error)
272+
type GenericWorkflowFunc[P any, R any] func(ctx context.Context, input P) (R, error)
138273
```
139274

140-
`P` and `R` must be concrete types (not `any`).
275+
To register a workflow call `dbos.RegisterWorkflow(dbosCtx, workflow)` after having initialized a DBOS Context. Workflows can only be registered before DBOS is launched.
141276

142-
Workflows must be registered with DBOS using the `WithWorkflow` method before DBOS is launched.
143277

144278
Workflows can run steps, which can be any function with the following signature:
145279
```golang
146-
type StepFunc[P any, R any] func(ctx context.Context, input P) (R, error)
280+
type GenericStepFunc[R any] func(ctx context.Context) (R, error)
147281
```
148282

149-
To run a step within a workflow, use `RunAsStep`. Importantly, you must pass to `RunAsStep` the context received in the workflow function.
283+
To run a step within a workflow, use `RunAsStep`. Importantly, you must pass to `RunAsStep` the context received in the workflow function (see examples above.)
150284

151285
The input and output of workflows and steps are memoized in your Postgres database for workflow recovery. Under the hood, DBOS uses the [encoding/gob](https://pkg.go.dev/encoding/gob) package for serialization (this means that only exported fields will be memoized and types without exported fields will generate an error.)
152286

153-
## Roadmap:
154-
* logging for DBOS internals -- consider accepting a user provided logger
155-
* OTel trace generation and export
156-
* OTel logs -- consider leveraging the user provided logger
157-
* config?
158-
* go doc
159-
* workflows send and recv
160-
* workflows set and get event
161-
* workflow cancellation maps
162-
* queue dedup
163-
* queue priority
164-
* workflow timeouts
165-
* DBOS Client
166-
* datasources & transactions
167-
168287
## Getting started
169288

170289
Install the DBOS Transact package in your program:
171290

172291
```shell
173-
github.com/dbos-inc/dbos-transact-go
292+
go get github.com/dbos-inc/dbos-transact-go
174293
```
175294

176-
You can store and export a Postgres connection string in the `DBOS_SYSTEM_DATABASE_URL` environment variable for DBOS to manage your workflows state. By default, DBOS will use `postgres://postgres:${PGPASSWORD}@localhost:5432/dbos?sslmode=disable`.
295+
You can store and export a Postgres connection string in the `DBOS_SYSTEM_DATABASE_URL` environment variable for DBOS to manage your workflows state. By default, DBOS will use `postgres://postgres:${PGPASSWORD}@localhost:5432/dbos?sslmode=disable`.
296+
297+
298+
## ⭐️ Like this project?
299+
300+
[Star it on GitHub](https://github.com/dbos-inc/dbos-transact-go)
301+
[![GitHub Stars](https://img.shields.io/github/stars/dbos-inc/dbos-transact-go?style=social)](https://github.com/dbos-inc/dbos-transact-go)

0 commit comments

Comments
 (0)