@@ -119,7 +119,21 @@ func createDatabaseIfNotExists(ctx context.Context, databaseURL string, logger *
119119//go:embed migrations/*.sql
120120var migrationFiles embed.FS
121121
122- const _DBOS_MIGRATION_TABLE = "dbos_schema_migrations"
122+ const (
123+ _DBOS_MIGRATION_TABLE = "dbos_schema_migrations"
124+
125+ // PostgreSQL error codes
126+ _PG_ERROR_UNIQUE_VIOLATION = "23505"
127+ _PG_ERROR_FOREIGN_KEY_VIOLATION = "23503"
128+
129+ // Notification channels
130+ _DBOS_NOTIFICATIONS_CHANNEL = "dbos_notifications_channel"
131+ _DBOS_WORKFLOW_EVENTS_CHANNEL = "dbos_workflow_events_channel"
132+
133+ // Database retry timeouts
134+ _DB_CONNECTION_RETRY_DELAY = 500 * time .Millisecond
135+ _DB_RETRY_INTERVAL = 1 * time .Second
136+ )
123137
124138func runMigrations (databaseURL string ) error {
125139 // Change the driver to pgx5
@@ -194,7 +208,7 @@ func newSystemDatabase(ctx context.Context, databaseURL string, logger *slog.Log
194208 return nil , fmt .Errorf ("failed to parse database URL: %v" , err )
195209 }
196210 config .OnNotification = func (c * pgconn.PgConn , n * pgconn.Notification ) {
197- if n .Channel == "dbos_notifications_channel" || n .Channel == "dbos_workflow_events_channel" {
211+ if n .Channel == _DBOS_NOTIFICATIONS_CHANNEL || n .Channel == _DBOS_WORKFLOW_EVENTS_CHANNEL {
198212 // Check if an entry exists in the map, indexed by the payload
199213 // If yes, broadcast on the condition variable so listeners can wake up
200214 if cond , exists := notificationsMap .Load (n .Payload ); exists {
@@ -245,7 +259,7 @@ func (s *sysDB) shutdown(ctx context.Context) {
245259 // Allow pgx health checks to complete
246260 // https://github.com/jackc/pgx/blob/15bca4a4e14e0049777c1245dba4c16300fe4fd0/pgxpool/pool.go#L417
247261 // These trigger go-leak alerts
248- time .Sleep (500 * time . Millisecond )
262+ time .Sleep (_DB_CONNECTION_RETRY_DELAY )
249263
250264 s .launched = false
251265}
@@ -380,7 +394,7 @@ func (s *sysDB) insertWorkflowStatus(ctx context.Context, input insertWorkflowSt
380394 )
381395 if err != nil {
382396 // Handle unique constraint violation for the deduplication ID (this should be the only case for a 23505)
383- if pgErr , ok := err .(* pgconn.PgError ); ok && pgErr .Code == "23505" {
397+ if pgErr , ok := err .(* pgconn.PgError ); ok && pgErr .Code == _PG_ERROR_UNIQUE_VIOLATION {
384398 return nil , newQueueDeduplicatedError (
385399 input .status .ID ,
386400 input .status .QueueName ,
@@ -924,7 +938,7 @@ func (s *sysDB) awaitWorkflowResult(ctx context.Context, workflowID string) (any
924938 err := row .Scan (& status , & outputString , & errorStr )
925939 if err != nil {
926940 if err == pgx .ErrNoRows {
927- time .Sleep (1 * time . Second )
941+ time .Sleep (_DB_RETRY_INTERVAL )
928942 continue
929943 }
930944 return nil , fmt .Errorf ("failed to query workflow status: %w" , err )
@@ -945,7 +959,7 @@ func (s *sysDB) awaitWorkflowResult(ctx context.Context, workflowID string) (any
945959 case WorkflowStatusCancelled :
946960 return output , newAwaitedWorkflowCancelledError (workflowID )
947961 default :
948- time .Sleep (1 * time . Second )
962+ time .Sleep (_DB_RETRY_INTERVAL )
949963 }
950964 }
951965}
@@ -1003,7 +1017,7 @@ func (s *sysDB) recordOperationResult(ctx context.Context, input recordOperation
10031017
10041018 if err != nil {
10051019 s .logger .Error ("RecordOperationResult Error occurred" , "error" , err )
1006- if pgErr , ok := err .(* pgconn.PgError ); ok && pgErr .Code == "23505" {
1020+ if pgErr , ok := err .(* pgconn.PgError ); ok && pgErr .Code == _PG_ERROR_UNIQUE_VIOLATION {
10071021 return newWorkflowConflictIDError (input .workflowID )
10081022 }
10091023 return err
@@ -1054,7 +1068,7 @@ func (s *sysDB) recordChildWorkflow(ctx context.Context, input recordChildWorkfl
10541068
10551069 if err != nil {
10561070 // Check for unique constraint violation (conflict ID error)
1057- if pgErr , ok := err .(* pgconn.PgError ); ok && pgErr .Code == "23505" {
1071+ if pgErr , ok := err .(* pgconn.PgError ); ok && pgErr .Code == _PG_ERROR_UNIQUE_VIOLATION {
10581072 return fmt .Errorf (
10591073 "child workflow %s already registered for parent workflow %s (operation ID: %d)" ,
10601074 input .childWorkflowID , input .parentWorkflowID , input .stepID )
@@ -1361,7 +1375,7 @@ func (s *sysDB) notificationListenerLoop(ctx context.Context) {
13611375 }()
13621376
13631377 s .logger .Info ("DBOS: Starting notification listener loop" )
1364- mrr := s .notificationListenerConnection .Exec (ctx , "LISTEN dbos_notifications_channel ; LISTEN dbos_workflow_events_channel" )
1378+ mrr := s .notificationListenerConnection .Exec (ctx , fmt . Sprintf ( "LISTEN %s ; LISTEN %s" , _DBOS_NOTIFICATIONS_CHANNEL , _DBOS_WORKFLOW_EVENTS_CHANNEL ) )
13651379 results , err := mrr .ReadAll ()
13661380 if err != nil {
13671381 s .logger .Error ("Failed to listen on notification channels" , "error" , err )
@@ -1399,7 +1413,7 @@ func (s *sysDB) notificationListenerLoop(ctx context.Context) {
13991413
14001414 // Other errors - log and retry. XXX eventually add exponential backoff + jitter
14011415 s .logger .Error ("Error waiting for notification" , "error" , err )
1402- time .Sleep (500 * time . Millisecond )
1416+ time .Sleep (_DB_CONNECTION_RETRY_DELAY )
14031417 continue
14041418 }
14051419 }
@@ -1472,7 +1486,7 @@ func (s *sysDB) send(ctx context.Context, input WorkflowSendInput) error {
14721486 _ , err = tx .Exec (ctx , insertQuery , input .DestinationID , topic , messageString )
14731487 if err != nil {
14741488 // Check for foreign key violation (destination workflow doesn't exist)
1475- if pgErr , ok := err .(* pgconn.PgError ); ok && pgErr .Code == "23503" {
1489+ if pgErr , ok := err .(* pgconn.PgError ); ok && pgErr .Code == _PG_ERROR_FOREIGN_KEY_VIOLATION {
14761490 return newNonExistentWorkflowError (input .DestinationID )
14771491 }
14781492 return fmt .Errorf ("failed to insert notification: %w" , err )
0 commit comments