# send.ludic - every frame: a batch when one is due, its answer when it lands, the wait doubled # when it failed, the queue on disk every few seconds. One request at a time; nothing waits on it. export function telemetry_tick(telemetry_st: mut TelemetryState) -> void { if not telemetry_st.telemetry__ready { return } telemetry__id_tick(telemetry_st) if not telemetry__enabled() { if telemetry_st.telemetry__n > 0 or telemetry_st.telemetry__h >= 0 { telemetry_forget(telemetry_st) } return } if not telemetry__can_send(telemetry_st) { return } let now = telemetry__now() let c = telemetry__conf(telemetry_st) if not telemetry_st.telemetry__clock_on { telemetry_st.telemetry__clock_on = true telemetry_st.telemetry__next_ms = now + c.flush_ms telemetry_st.telemetry__saved_ms = now } if now - telemetry_st.telemetry__saved_ms >= c.save_ms { telemetry_save(telemetry_st) telemetry_st.telemetry__saved_ms = now } if telemetry_st.telemetry__h >= 0 { telemetry__answer(telemetry_st, now) return } if telemetry_st.telemetry__n == 0 { return } let early = telemetry_st.telemetry__fails == 0 and telemetry_st.telemetry__n >= c.flush_at if early or now >= telemetry_st.telemetry__next_ms { telemetry_save(telemetry_st) telemetry__flush(telemetry_st, now) if telemetry_st.telemetry__fails == 0 { telemetry_st.telemetry__next_ms = now + c.flush_ms } } } function telemetry__answer(telemetry_st: mut TelemetryState, now: int) -> void { let st = telemetry__poll(telemetry_st.telemetry__h) if st < 0 { return } telemetry_st.telemetry__h = -1 if st > 0 { tr_drop(telemetry_st, telemetry_st.telemetry__sent_n) telemetry_st.telemetry__fails = 0 telemetry_st.telemetry__next_ms = now + telemetry__conf(telemetry_st).flush_ms telemetry__fact(telemetry_st, TELEMETRY_SENT, telemetry_st.telemetry__sent_n, st, 0) } else { telemetry__backoff(telemetry_st, now) telemetry__fact(telemetry_st, TELEMETRY_FAILED, telemetry_st.telemetry__sent_n, st, telemetry_st.telemetry__next_ms - now) } telemetry_save(telemetry_st) } function telemetry__flush(telemetry_st: mut TelemetryState, now: int) -> void { if telemetry_st.telemetry__h >= 0 or len(telemetry_st.telemetry__id) == 0 or telemetry_st.telemetry__n == 0 { return } let n = min(telemetry__conf(telemetry_st).batch, telemetry_st.telemetry__n) tb_batch(telemetry_st, n) let h = telemetry__send(telemetry_st, telemetry_st.telemetry__out.b, sb_len(telemetry_st.telemetry__out)) if h < 0 { telemetry__backoff(telemetry_st, now) telemetry__fact(telemetry_st, TELEMETRY_FAILED, n, 0, telemetry_st.telemetry__next_ms - now) return } telemetry_st.telemetry__h = h telemetry_st.telemetry__sent_n = n } # a failed send: wait twice as long next time, up to backoff_max doublings function telemetry__backoff(telemetry_st: mut TelemetryState, now: int) -> void { if telemetry_st.telemetry__fails < telemetry__conf(telemetry_st).backoff_max { telemetry_st.telemetry__fails += 1 } telemetry_st.telemetry__next_ms = now + (telemetry__conf(telemetry_st).flush_ms << telemetry_st.telemetry__fails) }