package gateway import ( "context" "math/rand" "time" "github.com/rs/zerolog" ) type Heartbeat interface { Start(ctx context.Context, interval time.Duration) ForceHeartbeat() Ack() } var _ Heartbeat = &HeartbeatImpl{} type HeartbeatImpl struct { ctx context.Context logger *zerolog.Logger gateway Gateway } func NewHeartbeat(logger *zerolog.Logger, gateway Gateway) *HeartbeatImpl { return &HeartbeatImpl{ ctx: nil, logger: logger, gateway: gateway, } } func (h *HeartbeatImpl) Start(ctx context.Context, interval time.Duration) { h.ctx = ctx go h.heartbeatRoutine(interval) } func (h *HeartbeatImpl) ForceHeartbeat() { h.gateway.Heartbeat() } func (h *HeartbeatImpl) Ack() { // What do we do here? h.logger.Debug().Msg("received heartbeat ack") } func (h *HeartbeatImpl) heartbeatRoutine(interval time.Duration) { h.logger.Debug().Msgf("beating heart every %dms", interval.Milliseconds()) // REF: heartbeat_interval * jitter jitter := rand.Intn(int(interval)) time.Sleep(time.Duration(jitter)) ticker := time.NewTicker(interval) defer ticker.Stop() for { h.logger.Debug().Msg("sending heartbeat") if err := h.gateway.Heartbeat(); err != nil { h.logger.Error().Err(err).Msg("failed to send heartbeat") } select { case <-h.ctx.Done(): return case <-ticker.C: continue } } }