Skip to content

Commit

Permalink
Retry fetching/loading events with limit upon failure
Browse files Browse the repository at this point in the history
  • Loading branch information
AbdeltwabMF committed Mar 30, 2024
1 parent bf2ab5e commit 3fe5af2
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 60 deletions.
36 changes: 18 additions & 18 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ func initLogger() (*os.File, error) {
return file, nil
}

func waitNextMinute(calendarName string) {
sd := time.Until(time.Now().Truncate(time.Minute).Add(time.Minute))

slog.Info("Waiting for the next minute to recheck events",
slog.String("time.sleep", sd.String()),
slog.String("calendar", calendarName),
)

time.Sleep(sd)
}

func main() {
file, err := initLogger()
if err != nil {
Expand All @@ -78,8 +89,9 @@ func main() {
gerrch := make(chan error)
lerrch := make(chan error)

go googlecal.Fetch(gch, gerrch)
go localcal.Load(lch, lerrch)
retryLimit := 32
go googlecal.Fetch(gch, gerrch, retryLimit)
go localcal.Load(lch, lerrch, retryLimit)

// Monitor Google calendar events.
go func() {
Expand All @@ -89,7 +101,7 @@ func main() {
case gevents = <-gch:
slog.Info("Read from Google calendar channel")
case err := <-gerrch:
slog.Error("Unable to fetch events: %v", err, slog.String("calendar", "Google calendar"))
slog.Error(fmt.Sprintf("Unable to fetch events: %v", err), slog.String("calendar", "Google calendar"))
break loop
default:
if gevents != nil {
Expand All @@ -108,13 +120,7 @@ func main() {
}
}
}

st := time.Until(time.Now().Truncate(time.Minute).Add(time.Minute))
slog.Info("Done iteration; time to sleep",
slog.String("sleep.time", st.String()),
slog.String("calendar", "Google calendar"),
)
time.Sleep(st)
waitNextMinute("Google calendar")
}
}
}()
Expand All @@ -127,7 +133,7 @@ func main() {
case levents = <-lch:
slog.Info("Read from Local calendar channel")
case err := <-lerrch:
slog.Error("Unable to load events: %v", err, slog.String("calendar", "Local calendar"))
slog.Error(fmt.Sprintf("Unable to load events: %v", err), slog.String("calendar", "Local calendar"))
break loop
default:
if levents != nil {
Expand All @@ -140,13 +146,7 @@ func main() {
}
}
}

st := time.Until(time.Now().Truncate(time.Minute).Add(time.Minute))
slog.Info("Done iteration; time to sleep",
slog.String("sleep.time", st.String()),
slog.String("calendar", "Local calendar"),
)
time.Sleep(st)
waitNextMinute("Local calendar")
}
}
}()
Expand Down
51 changes: 34 additions & 17 deletions internal/googlecal/googlecal.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,40 +125,56 @@ func initService() (*calendar.Service, error) {
return calendar.NewService(ctx, option.WithHTTPClient(client))
}

func waitNextHour() {
sd := time.Until(time.Now().Truncate(time.Hour).Add(time.Hour))

slog.Info("Waiting for the next hour to refetch events",
slog.String("time.sleep", sd.String()),
slog.String("calendar", CalendarName),
)

time.Sleep(sd)
}

// Fetch fetches calendar events and sends them through the provided channel.
// It periodically fetches events, sleeping until the beginning of the next hour between fetches.
func Fetch(ch chan<- *calendar.Events, errch chan<- error) {
func Fetch(ch chan<- *calendar.Events, errch chan<- error, retryLimit int) {
srv, err := initService()
if err != nil {
errch <- err
return
}

const (
maxEvents = 7
hoursInDay = 24
)

for {
now := time.Now()
events, err := srv.Events.List("primary").
// From now
TimeMin(now.Format(time.RFC3339)).
// Until end of the day
TimeMax(now.Truncate(24 * time.Hour).Add(24 * time.Hour).Format(time.RFC3339)).
// Fetch only 7 events; assuming calendar is busy and there is an event every 10min
MaxResults(7).
SingleEvents(true).
OrderBy("startTime").
Do()
var events *calendar.Events

for i := 0; i < retryLimit; i++ {
events, err = srv.Events.List("primary").
TimeMin(now.Format(time.RFC3339)).
TimeMax(now.Truncate(hoursInDay * time.Hour).Add(hoursInDay * time.Hour).Format(time.RFC3339)).
MaxResults(maxEvents).
SingleEvents(true).
OrderBy("startTime").
Do()

if err == nil {
break
}
}

if err != nil {
errch <- err
return
}

ch <- events
st := time.Until(now.Truncate(time.Hour).Add(time.Hour))
slog.Info("Wait until the beginning of the next hour",
slog.String("time.sleep", st.String()),
slog.String("calendar", CalendarName),
)
time.Sleep(st)
waitNextHour()
}
}

Expand All @@ -176,6 +192,7 @@ func Match(event *calendar.Event) (bool, error) {
slog.String("now.time", now.Format("15:04")),
slog.String("calendar", CalendarName),
)

return t.Format("15:04") == now.Format("15:04"), nil
}

Expand Down
64 changes: 39 additions & 25 deletions internal/localcal/localcal.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,49 +27,63 @@ type Events struct {
Items []*Event `json:"events"`
}

func waitNextHour() {
sd := time.Until(time.Now().Truncate(time.Hour).Add(time.Hour))

slog.Info("Waiting for the next hour to reload events",
slog.String("time.sleep", sd.String()),
slog.String("calendar", CalendarName),
)

time.Sleep(sd)
}

// Load loads events from the local calendar and sends them to the provided channel.
func Load(ch chan<- *Events, errch chan<- error) {
func Load(ch chan<- *Events, errch chan<- error, retryLimit int) {
d, err := platform.ConfigDir()
if err != nil {
errch <- err
return
}

file, err := os.OpenFile(filepath.Join(d, "config.json"), os.O_CREATE|os.O_RDONLY, 0640)
if err != nil {
errch <- err
return
}
defer file.Close()

for {
d, err := platform.ConfigDir()
if err != nil {
errch <- err
return
}
var events Events
var err error

file, err := os.OpenFile(filepath.Join(d, "config.json"), os.O_CREATE|os.O_RDONLY, 0640)
if err != nil {
errch <- err
return
for i := 0; i < retryLimit; i++ {
if err = json.NewDecoder(file).Decode(&events); err == nil {
break
}
}
defer file.Close()

var events Events
if err := json.NewDecoder(file).Decode(&events); err != nil {
if err != nil {
errch <- err
return
}

ch <- &events
st := time.Until(time.Now().Truncate(time.Hour).Add(time.Hour))
slog.Info("Wait until the beginning of the next hour",
slog.String("time.sleep", st.String()),
slog.String("calendar", CalendarName),
)
time.Sleep(st)
waitNextHour()
}
}

// Match checks if the given event matches the current time(hh:mm) and day.
func Match(event *Event) bool {
now := time.Now()
slog.Info("Matching event",
slog.String("event.time", event.Start.Time),
slog.String("now.time", now.Format("15:04")),
slog.String("calendar", CalendarName),
)

for _, d := range event.Start.Days {
if d == now.Weekday().String() {
slog.Info("Matching event",
slog.String("event.time", event.Start.Time),
slog.String("now.time", now.Format("15:04")),
slog.String("calendar", CalendarName),
)

return event.Start.Time == now.Format("15:04")
}
}
Expand Down

0 comments on commit 3fe5af2

Please sign in to comment.