chore: 并入 iotlcm-worker-svr 历史到 worker-svr/(原提交 96f0f4e)
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
package contract
|
||||
|
||||
import (
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/fullsync/supply"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RunMeta retains every existing queue metadata field and JSON tag. The
|
||||
// optional fields are additive; zero extension fields are legal on old runs.
|
||||
type RunMeta struct {
|
||||
SupplyObservation *supply.Snapshot `json:"supplyObservation,omitempty"`
|
||||
RunID string `json:"runId"`
|
||||
Operation string `json:"operation"`
|
||||
QueryMonth string `json:"queryMonth"`
|
||||
Status RunStatus `json:"status"`
|
||||
ProducerDone bool `json:"producerDone"`
|
||||
CurrentRound int `json:"currentRound"`
|
||||
ActiveCount int64 `json:"activeCount"`
|
||||
InflightCount int64 `json:"inflightCount"`
|
||||
NextRoundCount int64 `json:"nextRoundCount"`
|
||||
SuccessCount int64 `json:"successCount"`
|
||||
FailedCount int64 `json:"failedCount"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
ProducerCompletedAt time.Time `json:"producerCompletedAt"`
|
||||
CompletedAt time.Time `json:"completedAt"`
|
||||
|
||||
SyncType SyncType `json:"syncType,omitempty"`
|
||||
SourceRunID string `json:"sourceRunId,omitempty"`
|
||||
ReplayCycle int `json:"replayCycle,omitempty"`
|
||||
TriggerSource string `json:"triggerSource,omitempty"`
|
||||
ScannedCount int64 `json:"scannedCount,omitempty"`
|
||||
EnqueuedCount int64 `json:"enqueuedCount,omitempty"`
|
||||
UpstreamAttemptCount int64 `json:"upstreamAttemptCount,omitempty"`
|
||||
BatchCount int64 `json:"batchCount,omitempty"`
|
||||
MaxRound int `json:"maxRound,omitempty"`
|
||||
PendingFailureCount int64 `json:"pendingFailureCount,omitempty"`
|
||||
StartedAt *time.Time `json:"startedAt,omitempty"`
|
||||
ScanCompletedAt *time.Time `json:"scanCompletedAt,omitempty"`
|
||||
ArchiveStatus ArchiveStatus `json:"archiveStatus,omitempty"`
|
||||
ArchiveVersion int `json:"archiveVersion,omitempty"`
|
||||
ArchivePath string `json:"archivePath,omitempty"`
|
||||
ArchiveChecksum string `json:"archiveChecksum,omitempty"`
|
||||
ArchivedAt *time.Time `json:"archivedAt,omitempty"`
|
||||
ReplayDisposition ReplayDisposition `json:"replayDisposition,omitempty"`
|
||||
// Scope fields mirror zset.RunMeta. ScopeChannels == 0 means a full sync.
|
||||
ScopeChannels int `json:"scopeChannels,omitempty"`
|
||||
ScopeDigest string `json:"scopeDigest,omitempty"`
|
||||
ScopeChannelsSample string `json:"scopeChannelsSample,omitempty"`
|
||||
RoundStats []RoundStats `json:"rounds,omitempty"`
|
||||
}
|
||||
|
||||
func (m RunMeta) Validate() error {
|
||||
if err := ValidateID(m.RunID); err != nil {
|
||||
return err
|
||||
}
|
||||
if m.Status != "" && !m.Status.Valid() {
|
||||
return invalid("unknown run status")
|
||||
}
|
||||
if m.SyncType != "" && !m.SyncType.Valid() {
|
||||
return invalid("unknown sync type")
|
||||
}
|
||||
if m.ArchiveStatus != "" && !m.ArchiveStatus.Valid() {
|
||||
return invalid("unknown archive status")
|
||||
}
|
||||
if m.ReplayDisposition != "" && !m.ReplayDisposition.Valid() {
|
||||
return invalid("unknown replay disposition")
|
||||
}
|
||||
if m.SourceRunID != "" {
|
||||
if err := ValidateID(m.SourceRunID); err != nil {
|
||||
return err
|
||||
}
|
||||
if m.SourceRunID == m.RunID {
|
||||
return invalid("a run cannot replay itself")
|
||||
}
|
||||
}
|
||||
if m.SyncType == SyncReplay && (m.SourceRunID == "" || m.ReplayCycle < 1) {
|
||||
return invalid("replay requires sourceRunId and a positive replayCycle")
|
||||
}
|
||||
if m.SyncType == SyncNormal && (m.SourceRunID != "" || m.ReplayCycle != 0) {
|
||||
return invalid("normal runs cannot have replay provenance")
|
||||
}
|
||||
if m.MaxRound > 0 && m.CurrentRound > m.MaxRound {
|
||||
return invalid("currentRound exceeds maxRound")
|
||||
}
|
||||
return nonnegative(int64(m.CurrentRound), int64(m.MaxRound), int64(m.ReplayCycle), int64(m.ArchiveVersion), m.ActiveCount, m.InflightCount, m.NextRoundCount, m.SuccessCount, m.FailedCount, m.ScannedCount, m.EnqueuedCount, m.UpstreamAttemptCount, m.BatchCount, m.PendingFailureCount, int64(m.ScopeChannels))
|
||||
}
|
||||
|
||||
// ValidateAccounting is opt-in for runs whose extended counters are known.
|
||||
// Do not apply it to legacy metadata with a missing enqueuedCount. Inflight is
|
||||
// a subset of active and is never added to the completion equation.
|
||||
func (m RunMeta) ValidateAccounting() error {
|
||||
if err := m.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
remaining := m.EnqueuedCount
|
||||
for _, count := range []int64{m.SuccessCount, m.FailedCount, m.ActiveCount, m.NextRoundCount} {
|
||||
if count > remaining {
|
||||
return invalid("enqueuedCount does not balance run outcomes")
|
||||
}
|
||||
remaining -= count
|
||||
}
|
||||
if remaining != 0 || m.InflightCount > m.ActiveCount || m.PendingFailureCount > m.FailedCount {
|
||||
return invalid("inconsistent run counters")
|
||||
}
|
||||
if m.Status.Terminal() && (!m.ProducerDone || m.ActiveCount != 0 || m.NextRoundCount != 0) {
|
||||
return invalid("terminal run still has producer or queue work")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RoundStats contains only bounded aggregate fields. It must never grow with
|
||||
// the number of cards, channels, task IDs, upstream codes, or error messages.
|
||||
type RoundStats struct {
|
||||
RunID string `json:"runId"`
|
||||
Round int `json:"round"`
|
||||
EnqueuedCount int64 `json:"enqueuedCount"`
|
||||
ActiveCount int64 `json:"activeCount"`
|
||||
NextRoundCount int64 `json:"nextRoundCount"`
|
||||
SuccessCount int64 `json:"successCount"`
|
||||
FailedCount int64 `json:"failedCount"`
|
||||
UpstreamAttemptCount int64 `json:"upstreamAttemptCount"`
|
||||
BatchCount int64 `json:"batchCount"`
|
||||
StartedAt *time.Time `json:"startedAt,omitempty"`
|
||||
CompletedAt *time.Time `json:"completedAt,omitempty"`
|
||||
ErrorCounts ErrorCounts `json:"errorCounts"`
|
||||
}
|
||||
|
||||
func (s RoundStats) Validate() error {
|
||||
if err := ValidateID(s.RunID); err != nil {
|
||||
return err
|
||||
}
|
||||
if s.Round < 1 {
|
||||
return invalid("round must be positive")
|
||||
}
|
||||
if err := nonnegative(s.EnqueuedCount, s.ActiveCount, s.NextRoundCount, s.SuccessCount, s.FailedCount, s.UpstreamAttemptCount, s.BatchCount); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := s.ErrorCounts.Normalized()
|
||||
return err
|
||||
}
|
||||
|
||||
type RunView struct {
|
||||
Source RunSource `json:"source"`
|
||||
Run RunMeta `json:"run"`
|
||||
Rounds []RoundStats `json:"rounds,omitempty"`
|
||||
}
|
||||
Reference in New Issue
Block a user