@@ -54,7 +54,7 @@ type systemDatabase interface {
5454 getEvent (ctx context.Context , input getEventInput ) (any , error )
5555
5656 // Timers (special steps)
57- sleep (ctx context.Context , duration time. Duration ) (time.Duration , error )
57+ sleep (ctx context.Context , input sleepInput ) (time.Duration , error )
5858
5959 // Queues
6060 dequeueWorkflows (ctx context.Context , input dequeueWorkflowsInput ) ([]dequeuedWorkflow , error )
@@ -111,7 +111,7 @@ func createDatabaseIfNotExists(ctx context.Context, databaseURL string, logger *
111111 if err != nil {
112112 return newInitializationError (fmt .Sprintf ("failed to create database %s: %v" , dbName , err ))
113113 }
114- logger .Info ("Database created" , "name" , dbName )
114+ logger .Debug ("Database created" , "name" , dbName )
115115 }
116116
117117 return nil
@@ -304,7 +304,7 @@ func (s *sysDB) launch(ctx context.Context) {
304304}
305305
306306func (s * sysDB ) shutdown (ctx context.Context , timeout time.Duration ) {
307- s .logger .Info ("DBOS: Closing system database connection pool" )
307+ s .logger .Debug ("DBOS: Closing system database connection pool" )
308308 if s .pool != nil {
309309 s .pool .Close ()
310310 }
@@ -319,7 +319,7 @@ func (s *sysDB) shutdown(ctx context.Context, timeout time.Duration) {
319319
320320 if s .launched {
321321 // Wait for the notification loop to exit
322- s .logger .Info ("DBOS: Waiting for notification listener loop to finish" )
322+ s .logger .Debug ("DBOS: Waiting for notification listener loop to finish" )
323323 select {
324324 case <- s .notificationLoopDone :
325325 case <- time .After (timeout ):
@@ -1438,10 +1438,17 @@ func (s *sysDB) getWorkflowSteps(ctx context.Context, workflowID string) ([]Step
14381438 return steps , nil
14391439}
14401440
1441+ type sleepInput struct {
1442+ duration time.Duration // Duration to sleep
1443+ skipSleep bool // If true, the function will not actually sleep (useful for testing)
1444+ }
1445+
14411446// Sleep is a special type of step that sleeps for a specified duration
14421447// A wakeup time is computed and recorded in the database
14431448// If we sleep is re-executed, it will only sleep for the remaining duration until the wakeup time
1444- func (s * sysDB ) sleep (ctx context.Context , duration time.Duration ) (time.Duration , error ) {
1449+ // sleep can be called within other special steps (e.g., getEvent, recv) to provide durable sleep
1450+
1451+ func (s * sysDB ) sleep (ctx context.Context , input sleepInput ) (time.Duration , error ) {
14451452 functionName := "DBOS.sleep"
14461453
14471454 // Get workflow state from context
@@ -1486,9 +1493,9 @@ func (s *sysDB) sleep(ctx context.Context, duration time.Duration) (time.Duratio
14861493 }
14871494 } else {
14881495 // First execution: calculate and record the end time
1489- s .logger .Debug ("Durable sleep" , "stepID" , stepID , "duration" , duration )
1496+ s .logger .Debug ("Durable sleep" , "stepID" , stepID , "duration" , input . duration )
14901497
1491- endTime = time .Now ().Add (duration )
1498+ endTime = time .Now ().Add (input . duration )
14921499
14931500 // Record the operation result with the calculated end time
14941501 recordInput := recordOperationResultDBInput {
@@ -1512,8 +1519,10 @@ func (s *sysDB) sleep(ctx context.Context, duration time.Duration) (time.Duratio
15121519 // Calculate remaining duration until wake up time
15131520 remainingDuration := max (0 , time .Until (endTime ))
15141521
1515- // Actually sleep for the remaining duration
1516- time .Sleep (remainingDuration )
1522+ if ! input .skipSleep {
1523+ // Actually sleep for the remaining duration
1524+ time .Sleep (remainingDuration )
1525+ }
15171526
15181527 return remainingDuration , nil
15191528}
@@ -1527,7 +1536,7 @@ func (s *sysDB) notificationListenerLoop(ctx context.Context) {
15271536 s .notificationLoopDone <- struct {}{}
15281537 }()
15291538
1530- s .logger .Info ("DBOS: Starting notification listener loop" )
1539+ s .logger .Debug ("DBOS: Starting notification listener loop" )
15311540 mrr := s .notificationListenerConnection .Exec (ctx , fmt .Sprintf ("LISTEN %s; LISTEN %s" , _DBOS_NOTIFICATIONS_CHANNEL , _DBOS_WORKFLOW_EVENTS_CHANNEL ))
15321541 results , err := mrr .ReadAll ()
15331542 if err != nil {
@@ -1555,7 +1564,7 @@ func (s *sysDB) notificationListenerLoop(ctx context.Context) {
15551564 if err != nil {
15561565 // Context cancellation
15571566 if errors .Is (err , context .Canceled ) || errors .Is (err , context .DeadlineExceeded ) {
1558- s .logger .Info ("Notification listener loop exiting due to context cancellation" , "cause" , context .Cause (ctx ), "error" , err )
1567+ s .logger .Debug ("Notification listener loop exiting due to context cancellation" , "cause" , context .Cause (ctx ), "error" , err )
15591568 return
15601569 }
15611570
@@ -1750,10 +1759,18 @@ func (s *sysDB) recv(ctx context.Context, input recvInput) (any, error) {
17501759 close (done )
17511760 }()
17521761
1762+ timeout , err := s .sleep (ctx , sleepInput {
1763+ duration : input .Timeout ,
1764+ skipSleep : true ,
1765+ })
1766+ if err != nil {
1767+ return nil , fmt .Errorf ("failed to sleep before recv timeout: %w" , err )
1768+ }
1769+
17531770 select {
17541771 case <- done :
17551772 s .logger .Debug ("Received notification on condition variable" , "payload" , payload )
1756- case <- time .After (input . Timeout ):
1773+ case <- time .After (timeout ):
17571774 s .logger .Warn ("Recv() timeout reached" , "payload" , payload , "timeout" , input .Timeout )
17581775 }
17591776 }
@@ -1969,11 +1986,21 @@ func (s *sysDB) getEvent(ctx context.Context, input getEventInput) (any, error)
19691986 close (done )
19701987 }()
19711988
1989+ timeout := input .Timeout
1990+ if isInWorkflow {
1991+ timeout , err = s .sleep (ctx , sleepInput {
1992+ duration : input .Timeout ,
1993+ skipSleep : true ,
1994+ })
1995+ if err != nil {
1996+ return nil , fmt .Errorf ("failed to sleep before getEvent timeout: %w" , err )
1997+ }
1998+ }
1999+
19722000 select {
19732001 case <- done :
19742002 // Received notification
1975- case <- time .After (input .Timeout ):
1976- // Timeout reached
2003+ case <- time .After (timeout ):
19772004 s .logger .Warn ("GetEvent() timeout reached" , "target_workflow_id" , input .TargetWorkflowID , "key" , input .Key , "timeout" , input .Timeout )
19782005 case <- ctx .Done ():
19792006 return nil , fmt .Errorf ("context cancelled while waiting for event: %w" , ctx .Err ())
0 commit comments