This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

feat(schedule restoration): allow eager schedule restoration, make startup recovery configurable

+57 -38
+12 -8
config.go
··· 12 12 ) 13 13 14 14 type Configuration struct { 15 - Port int `env:"PORT" envDefault:"8080"` 16 - ChurrosDatabaseURL string `env:"DATABASE_URL"` 17 - RedisURL string `env:"REDIS_URL"` 18 - VapidPublicKey string `env:"PUBLIC_VAPID_KEY"` 19 - VapidPrivateKey string `env:"VAPID_PRIVATE_KEY"` 20 - ContactEmail string `env:"CONTACT_EMAIL"` 21 - FirebaseServiceAccount string `env:"FIREBASE_SERVICE_ACCOUNT"` 22 - AppPackageId string `env:"APP_PACKAGE_ID" envDefault:"app.churros"` 15 + ChurrosDatabaseURL string `env:"DATABASE_URL"` 16 + RedisURL string `env:"REDIS_URL"` 17 + VapidPublicKey string `env:"PUBLIC_VAPID_KEY"` 18 + VapidPrivateKey string `env:"VAPID_PRIVATE_KEY"` 19 + ContactEmail string `env:"CONTACT_EMAIL"` 20 + FirebaseServiceAccount string `env:"FIREBASE_SERVICE_ACCOUNT"` 21 + StartupScheduleRestoration string `env:"STARTUP_SCHEDULE_RESTORATION" envDefault:"enabled"` 22 + AppPackageId string `env:"APP_PACKAGE_ID" envDefault:"app.churros"` 23 23 } 24 24 25 25 func LoadConfiguration() (Configuration, error) { ··· 35 35 err := env.Parse(&config) 36 36 if err != nil { 37 37 return Configuration{}, fmt.Errorf("could not load env variables: %w", err) 38 + } 39 + 40 + if config.StartupScheduleRestoration != "enabled" && config.StartupScheduleRestoration != "disabled" && config.StartupScheduleRestoration != "eager" { 41 + return Configuration{}, fmt.Errorf("invalid value for STARTUP_SCHEDULE_RESTORATION: %q, should be one of \"enabled\", \"disabled\", \"eager\"", config.StartupScheduleRestoration) 38 42 } 39 43 40 44 ll.Log("Loaded", "green", "configuration from environment")
+18 -16
events.go
··· 12 12 EventShowScheduledJobs Event = "show_scheduled_jobs" 13 13 EventSaveSchedule Event = "save_schedule" 14 14 EventRestoreSchedule Event = "restore_schedule" 15 - EventClearSchedule Event = "clear_schedule" 16 - EventNewPost Event = "new_post" 17 - EventGodchildRequest Event = "godchild_request" 18 - EventNewComment Event = "new_comment" 19 - EventCommentReply Event = "comment_reply" 20 - EventCustom Event = "custom" 21 - EventTest Event = "test" 22 - EventGodchildAccepted Event = "godchild_accepted" 23 - EventGodchildRejected Event = "godchild_rejected" 24 - EventPendingSignup Event = "pending_signup" 25 - EventLoginStuck Event = "login_stuck" 26 - EventBookingPaid Event = "booking_paid" 27 - EventContributionPaid Event = "contribution_paid" 28 - EventShotgunOpensSoon Event = "shotgun_opens_soon" 29 - EventShotgunClosesSoon Event = "shotgun_closes_soon" 15 + // Like restore_schedule, but also re-schedules events that have send_at in the past 16 + EventRestoreScheduleEager Event = "restore_schedule_eager" 17 + EventClearSchedule Event = "clear_schedule" 18 + EventNewPost Event = "new_post" 19 + EventGodchildRequest Event = "godchild_request" 20 + EventNewComment Event = "new_comment" 21 + EventCommentReply Event = "comment_reply" 22 + EventCustom Event = "custom" 23 + EventTest Event = "test" 24 + EventGodchildAccepted Event = "godchild_accepted" 25 + EventGodchildRejected Event = "godchild_rejected" 26 + EventPendingSignup Event = "pending_signup" 27 + EventLoginStuck Event = "login_stuck" 28 + EventBookingPaid Event = "booking_paid" 29 + EventContributionPaid Event = "contribution_paid" 30 + EventShotgunOpensSoon Event = "shotgun_opens_soon" 31 + EventShotgunClosesSoon Event = "shotgun_closes_soon" 30 32 ) 31 33 32 34 type Message struct { ··· 35 37 // When to push the notification 36 38 SendAt time.Time `json:"send_at"` 37 39 // Type of event that triggered the notification 38 - Event Event `json:"event" jsonschema:"enum=save_schedule,enum=clear_schedule,enum=clear_stored_schedule,enum=restore_schedule,enum=clear_scheduled_jobs,enum=show_scheduled_jobs,enum=new_post,enum=godchild_request,enum=new_comment,enum=comment_reply,enum=custom,enum=test,enum=godchild_accepted,enum=godchild_rejected,enum=pending_signup,enum=login_stuck,enum=booking_paid,enum=contribution_paid,enum=shotgun_opens_soon,enum=shotgun_closes_soon"` 40 + Event Event `json:"event" jsonschema:"enum=save_schedule,enum=clear_schedule,enum=clear_stored_schedule,enum=restore_schedule,enum=restore_schedule_eager,enum=clear_scheduled_jobs,enum=show_scheduled_jobs,enum=new_post,enum=godchild_request,enum=new_comment,enum=comment_reply,enum=custom,enum=test,enum=godchild_accepted,enum=godchild_rejected,enum=pending_signup,enum=login_stuck,enum=booking_paid,enum=contribution_paid,enum=shotgun_opens_soon,enum=shotgun_closes_soon"` 39 41 // Churros ID of the ressource (the ticket, the post, the comment, etc) 40 42 // Used to determine to whom the notification should be sent 41 43 // For godchild_request, this is not a user id, but a godparent request id.
+15 -4
scheduler.go
··· 26 26 } 27 27 28 28 // RestoreSchedule restores the scheduled messages from Redis to memory 29 - func RestoreSchedule() error { 30 - ll.Log("Restoring", "blue", "schedule from Redis") 29 + func RestoreSchedule(eager bool) error { 30 + if eager { 31 + ll.Log("Restoring", "blue", "schedule from Redis [red][bold]eagerly[reset]") 32 + } else { 33 + ll.Log("Restoring", "blue", "schedule from Redis") 34 + 35 + } 31 36 keys, err := redisClient.Keys(context.Background(), "notella:message:*").Result() 32 37 if err != nil { 33 38 return fmt.Errorf("while getting notella:message:* keys from redis: %w", err) ··· 47 52 return fmt.Errorf("while restoring schedule: could not unmarshal value for Redis key %s: %w", key, err) 48 53 } 49 54 50 - if job.SendAt.Before(time.Now()) { 55 + if !eager && job.SendAt.Before(time.Now()) { 51 56 ll.Warn("skipping restoration of %s because it's in the past: %#v", job.Id, job) 52 57 continue 53 58 } ··· 130 135 DisplaySchedule() 131 136 132 137 case EventRestoreSchedule: 133 - err := RestoreSchedule() 138 + err := RestoreSchedule(false) 139 + if err != nil { 140 + ll.ErrorDisplay("could not restore schedule", err) 141 + } 142 + 143 + case EventRestoreScheduleEager: 144 + err := RestoreSchedule(true) 134 145 if err != nil { 135 146 ll.ErrorDisplay("could not restore schedule", err) 136 147 }
+12 -10
server/main.go
··· 27 27 28 28 ll.Info("Server time is %s", time.Now().Format("2006-01-02 15:04:05 -07:00:00")) 29 29 ll.Info("Running with config ") 30 - ll.Log("", "reset", "port: [bold]%d[reset]", config.Port) 31 - ll.Log("", "reset", "contact email: [bold]%s[reset]", config.ContactEmail) 32 - ll.Log("", "reset", "Churros DB URL: [bold]%s[reset]", redactURL(config.ChurrosDatabaseURL)) 33 - ll.Log("", "reset", "Redis URL: [bold]%s[reset]", redactURL(config.RedisURL)) 34 - ll.Log("", "reset", "App Package ID: [bold]%s[reset]", config.AppPackageId) 30 + ll.Log("", "reset", "Schedule recovery: [bold][dim]at startup [reset][bold]%s[reset]", config.StartupScheduleRestoration) 31 + ll.Log("", "reset", "contact email: [bold]%s[reset]", config.ContactEmail) 32 + ll.Log("", "reset", "Churros DB URL: [bold]%s[reset]", redactURL(config.ChurrosDatabaseURL)) 33 + ll.Log("", "reset", "Redis URL: [bold]%s[reset]", redactURL(config.RedisURL)) 34 + ll.Log("", "reset", "App Package ID: [bold]%s[reset]", config.AppPackageId) 35 35 if config.VapidPublicKey != "" && config.VapidPrivateKey != "" { 36 - ll.Log("", "reset", "VAPID keys: [bold][green]set[reset]") 36 + ll.Log("", "reset", "VAPID keys: [bold][green]set[reset]") 37 37 } else { 38 - ll.Log("", "reset", "VAPID keys: [bold][red]not set[reset]") 38 + ll.Log("", "reset", "VAPID keys: [bold][red]not set[reset]") 39 39 } 40 40 if config.HasValidFirebaseServiceAccount() { 41 - ll.Log("", "reset", "Firebase: [bold][green]configured[reset]") 41 + ll.Log("", "reset", "Firebase: [bold][green]configured[reset]") 42 42 } else { 43 - ll.Log("", "reset", "Firebase: [bold][red]unconfigured[reset]") 43 + ll.Log("", "reset", "Firebase: [bold][red]unconfigured[reset]") 44 44 } 45 45 fmt.Println() 46 46 47 - notella.RestoreSchedule() 47 + if config.StartupScheduleRestoration != "disabled" { 48 + notella.RestoreSchedule(config.StartupScheduleRestoration == "eager") 49 + } 48 50 notella.DisplaySchedule() 49 51 50 52 ll.Info("starting scheduler")