fixes
This commit is contained in:
parent
21a0856587
commit
e8dc967e6a
26 changed files with 1373 additions and 481 deletions
|
|
@ -2,43 +2,76 @@ package store
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
// BroadcastStore persists system broadcast campaigns and their durable
|
||||
// per-recipient delivery outbox.
|
||||
//
|
||||
// A BroadcastTargetAll campaign is not fully enumerated at creation:
|
||||
// CreateBroadcast only snapshots the target user-id range (the current max
|
||||
// user id) and returns. MaterializeBroadcastRecipients then advances that
|
||||
// campaign's enumeration a bounded batch at a time, so a huge user base
|
||||
// never blocks the admin's create call, or any one worker cycle, on a
|
||||
// single giant INSERT. ClaimBroadcastRecipients/ReleaseBroadcastRecipient/
|
||||
// CompleteBroadcastRecipient implement a lease-based handoff for the
|
||||
// delivery half of the cycle: a worker claims a bounded batch of eligible
|
||||
// rows under a time-limited lease, and either completes or releases each
|
||||
// one it processes. A lease that is never renewed simply expires, so a
|
||||
// worker crash mid-cycle cannot strand a row in 'processing' forever, and
|
||||
// two workers can never believe they both hold the same row's lease at once.
|
||||
type BroadcastStore interface {
|
||||
// CreateBroadcast inserts the broadcast row and one pending recipient row
|
||||
// per id in recipientUserIDs, in a single transaction: a broadcast with
|
||||
// zero recipients (an empty "selected" list, or an "all" snapshot taken
|
||||
// when there happen to be no eligible users) is rejected with
|
||||
// domain.ErrBroadcastNoRecipients rather than created empty.
|
||||
CreateBroadcast(ctx context.Context, message string, targetMode domain.BroadcastTargetMode, recipientUserIDs []int64, createdBy string) (domain.Broadcast, error)
|
||||
// PendingBroadcastRecipients returns undelivered outbox rows across every
|
||||
// broadcast, oldest first, each carrying its broadcast's message text so
|
||||
// the worker can send without a second round trip per row.
|
||||
PendingBroadcastRecipients(ctx context.Context, limit int) ([]PendingBroadcastRecipient, error)
|
||||
// MarkBroadcastRecipientSent closes a recipient row as delivered.
|
||||
MarkBroadcastRecipientSent(ctx context.Context, recipientID int64) error
|
||||
// MarkBroadcastRecipientFailed records a failed attempt. The row stays
|
||||
// 'pending' (retried on the next cycle) until attempts reaches
|
||||
// domain.MaxBroadcastRecipientAttempts, at which point it becomes the
|
||||
// terminal 'failed' status.
|
||||
MarkBroadcastRecipientFailed(ctx context.Context, recipientID int64, reason string) error
|
||||
// ListBroadcasts pages broadcasts newest-first, each with sent/failed
|
||||
// counts derived live from its recipient rows.
|
||||
// PreviewBroadcastRecipients validates and counts the intended recipient
|
||||
// set without creating anything -- for "selected" mode this also
|
||||
// validates every id names a real, non-bot, non-system account.
|
||||
PreviewBroadcastRecipients(ctx context.Context, mode domain.BroadcastTargetMode, selectedUserIDs []int64) (int64, error)
|
||||
// CreateBroadcast inserts the broadcast row. For BroadcastTargetAll this
|
||||
// only snapshots the current max user id and target count; no recipient
|
||||
// rows are inserted here (see MaterializeBroadcastRecipients). For
|
||||
// BroadcastTargetSelected, the given ids are validated and their
|
||||
// recipient rows are inserted immediately, since that list is already
|
||||
// bounded by domain.MaxBroadcastSelectedRecipients.
|
||||
CreateBroadcast(ctx context.Context, message string, entities []domain.MessageEntity, mode domain.BroadcastTargetMode, selectedUserIDs []int64, createdBy string) (domain.Broadcast, error)
|
||||
// MaterializeBroadcastRecipients advances one "all"-mode campaign's
|
||||
// enumeration by up to limit newly-inserted recipient rows, and reports
|
||||
// how many were inserted. A campaign with nothing left to enumerate (or
|
||||
// no "all"-mode campaign still enumerating at all) returns 0, nil.
|
||||
MaterializeBroadcastRecipients(ctx context.Context, limit int) (int, error)
|
||||
// ClaimBroadcastRecipients atomically leases up to limit eligible rows
|
||||
// (pending, or processing under an expired lease) to leaseToken for
|
||||
// lease, returning each claim together with its broadcast's message and
|
||||
// entities so the caller can deliver without a second round trip.
|
||||
ClaimBroadcastRecipients(ctx context.Context, leaseToken string, limit int, lease time.Duration) ([]BroadcastRecipientClaim, error)
|
||||
// CompleteBroadcastRecipient closes a claimed row as delivered, recording
|
||||
// the message identifiers the send produced, and advances its
|
||||
// broadcast's sent_count. It is a no-op returning
|
||||
// domain.ErrBroadcastLeaseLost if the claim's lease was lost (expired
|
||||
// and reclaimed, or otherwise no longer matches) in the meantime --
|
||||
// safe to call even after a duplicate/idempotent resend, since the
|
||||
// caller is expected to tolerate that error.
|
||||
CompleteBroadcastRecipient(ctx context.Context, claim BroadcastRecipientClaim, privateMessageID int64, messageBoxID int, pts int) error
|
||||
// ReleaseBroadcastRecipient returns a claimed row to 'pending' (with
|
||||
// backoff) after a failed delivery attempt, or to the terminal 'failed'
|
||||
// once domain.MaxBroadcastRecipientAttempts is reached, and advances its
|
||||
// broadcast's failed_count in that terminal case.
|
||||
ReleaseBroadcastRecipient(ctx context.Context, claim BroadcastRecipientClaim, cause string) error
|
||||
// ListBroadcasts pages broadcasts newest-first.
|
||||
ListBroadcasts(ctx context.Context, beforeID int64, limit int) ([]domain.Broadcast, bool, error)
|
||||
// BroadcastByID returns one broadcast with derived counts.
|
||||
// BroadcastByID returns one broadcast.
|
||||
BroadcastByID(ctx context.Context, id int64) (domain.Broadcast, bool, error)
|
||||
}
|
||||
|
||||
// PendingBroadcastRecipient is one undelivered outbox row, joined with its
|
||||
// broadcast's message text.
|
||||
type PendingBroadcastRecipient struct {
|
||||
// BroadcastRecipientClaim is one recipient row leased for delivery, carrying
|
||||
// its broadcast's message text and entities so the worker doesn't need a
|
||||
// second lookup before sending.
|
||||
type BroadcastRecipientClaim struct {
|
||||
RecipientID int64
|
||||
BroadcastID int64
|
||||
UserID int64
|
||||
Attempts int
|
||||
LeaseToken string
|
||||
Message string
|
||||
Entities []domain.MessageEntity
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue