mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-05 16:02:48 +00:00
111 lines
5.0 KiB
Go
111 lines
5.0 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// SyncPolicy is the fair-use budget a mailbox syncs under. The control plane
|
|
// resolves it when the mailbox is loaded onto a worker (instance settings,
|
|
// see internal/app/instancesettings) and ships it inside ADD_EMAIL, so the
|
|
// worker never needs to read configuration of its own.
|
|
type SyncPolicy struct {
|
|
// BackfillDays is how far back the initial import reaches.
|
|
BackfillDays int `json:"backfill_days" avro:"backfill_days"`
|
|
// BackfillMessages caps how many messages the initial import stores.
|
|
BackfillMessages int `json:"backfill_messages" avro:"backfill_messages"`
|
|
// DailyMessages caps new (live) messages stored per UTC day. Replies to
|
|
// the mailbox's own sends have a separate budget of the same size.
|
|
DailyMessages int `json:"daily_messages" avro:"daily_messages"`
|
|
// OrgDailyMessages caps new plus backfilled messages stored across the
|
|
// whole organization per UTC day.
|
|
OrgDailyMessages int `json:"org_daily_messages" avro:"org_daily_messages"`
|
|
}
|
|
|
|
// SyncBackfillStatus is where the initial import stands.
|
|
type SyncBackfillStatus string
|
|
|
|
const (
|
|
// SyncBackfillPending: the mailbox is loaded but the import has not run yet.
|
|
SyncBackfillPending SyncBackfillStatus = "pending"
|
|
// SyncBackfillRunning: the import is walking history and may be paced.
|
|
SyncBackfillRunning SyncBackfillStatus = "running"
|
|
// SyncBackfillComplete: the window is exhausted or the cap was reached.
|
|
SyncBackfillComplete SyncBackfillStatus = "complete"
|
|
)
|
|
|
|
// SyncFolderCursor is the resumable position inside one folder of a backfill.
|
|
// IMAP keys folders by UIDVALIDITY and walks UIDs downward; Graph keys by
|
|
// well-known folder name and follows @odata.nextLink; Gmail has no folders and
|
|
// uses SyncCursor.PageToken.
|
|
type SyncFolderCursor struct {
|
|
// Next is an opaque continuation (Graph nextLink).
|
|
Next string `json:"next,omitempty" avro:"next"`
|
|
// UID is the lowest UID already imported; the walk continues below it.
|
|
UID uint32 `json:"uid,omitempty" avro:"uid"`
|
|
// Done marks the folder exhausted for this window.
|
|
Done bool `json:"done,omitempty" avro:"done"`
|
|
}
|
|
|
|
// SyncCursor is the resumable position of a mailbox's backfill. It is stored
|
|
// as jsonb: read-then-execute state that is never filtered in SQL.
|
|
type SyncCursor struct {
|
|
// PageToken is Gmail's messages.list continuation.
|
|
PageToken string `json:"page_token,omitempty" avro:"page_token"`
|
|
// Folders is the per-folder position for IMAP and Graph.
|
|
Folders map[string]SyncFolderCursor `json:"folders,omitempty" avro:"folders"`
|
|
}
|
|
|
|
// SyncState is what the platform knows about a mailbox's sync: backfill
|
|
// progress, whether fair use is holding it, and when it last ran. The worker
|
|
// owns the live copy and relays every change as SYNC_STATE; the consumer
|
|
// persists it and the loader hands it back on the next (re)assignment.
|
|
type SyncState struct {
|
|
BackfillStatus SyncBackfillStatus `json:"backfill_status" avro:"backfill_status"`
|
|
BackfillCursor SyncCursor `json:"backfill_cursor" avro:"backfill_cursor"`
|
|
// BackfillSynced counts messages the import has stored so far.
|
|
BackfillSynced int `json:"backfill_synced" avro:"backfill_synced"`
|
|
// BackfillSince is the cutoff the running import uses; fixed at start so
|
|
// a later settings change does not move the goalposts mid-walk.
|
|
BackfillSince *time.Time `json:"backfill_since,omitempty" avro:"backfill_since"`
|
|
BackfillStartedAt *time.Time `json:"backfill_started_at,omitempty" avro:"backfill_started_at"`
|
|
BackfillCompletedAt *time.Time `json:"backfill_completed_at,omitempty" avro:"backfill_completed_at"`
|
|
|
|
// ThrottledUntil is set while fair use is deferring live mail; nil when
|
|
// the mailbox is within budget.
|
|
ThrottledUntil *time.Time `json:"throttled_until,omitempty" avro:"throttled_until"`
|
|
// ThrottleReason names the exhausted budget (see SyncThrottle* constants).
|
|
ThrottleReason string `json:"throttle_reason,omitempty" avro:"throttle_reason"`
|
|
// Deferred counts live messages currently waiting on budget: seen on the
|
|
// server but not yet stored. Drops back to zero once they are admitted.
|
|
Deferred int `json:"deferred" avro:"deferred"`
|
|
|
|
LastSyncedAt *time.Time `json:"last_synced_at,omitempty" avro:"last_synced_at"`
|
|
}
|
|
|
|
// Throttle reasons, stable strings the dashboard maps to copy.
|
|
const (
|
|
SyncThrottleBurst = "burst"
|
|
SyncThrottleHourly = "hourly"
|
|
SyncThrottleDaily = "daily"
|
|
SyncThrottleOrgDaily = "org_daily"
|
|
SyncThrottlePriorityFull = "priority_daily"
|
|
)
|
|
|
|
// AddWorkerEmailSyncData seeds a mailbox's sync on the worker: the budget it
|
|
// runs under and where a previous worker left off. State is nil on first
|
|
// connect.
|
|
type AddWorkerEmailSyncData struct {
|
|
Policy SyncPolicy `json:"policy" avro:"policy"`
|
|
State *SyncState `json:"state" avro:"state"`
|
|
}
|
|
|
|
// JobEventSyncState is the worker's SYNC_STATE relay: the full current state,
|
|
// not a delta, so a lost event is repaired by the next one.
|
|
type JobEventSyncState struct {
|
|
UserID uuid.UUID `json:"user_id"`
|
|
EmailID uuid.UUID `json:"email_id"`
|
|
State SyncState `json:"state"`
|
|
}
|