Files
wehub-resource-sync f99010fae1
Desktop Artifacts / Desktop Build (Linux) (push) Waiting to run
Desktop Artifacts / Desktop Build (Windows) (push) Waiting to run
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Waiting to run
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

62 lines
2.0 KiB
Go

package sync
import (
"log"
"sync/atomic"
"time"
)
// PhaseStats accumulates wall-clock time spent in each phase of the
// bulk-write hot path during a single sync pass. Each field is a
// nanosecond total. Fields are exported and read after the pass to keep
// the per-batch increment fast.
//
// The bulk path serializes a producer-consumer pipeline: worker
// goroutines parse files and the single collectAndBatch goroutine
// preps each result, scans for secrets, computes signals, and writes
// the batch. PhaseStats measures the time spent in each step of that
// consumer so a profile-driven analysis can attribute the wall clock.
type PhaseStats struct {
PrepNanos atomic.Int64 // prepareSessionWrite (toDBMessages, toDBSession, ...)
ScanNanos atomic.Int64 // computeSignalsAndSecrets (regex scan)
WriteNanos atomic.Int64 // db.WriteSessionBatch (one DB tx per batch)
Batches atomic.Int64
BatchedWrites atomic.Int64 // sessions written via bulk batches
WriteBatchSize atomic.Int64 // sum of len(writes) across all batches
}
// Reset zeroes every counter. Called at the start of each sync pass so
// stats reflect only that pass.
func (p *PhaseStats) Reset() {
p.PrepNanos.Store(0)
p.ScanNanos.Store(0)
p.WriteNanos.Store(0)
p.Batches.Store(0)
p.BatchedWrites.Store(0)
p.WriteBatchSize.Store(0)
}
// Log emits a single-line summary of accumulated phase totals. It is
// a no-op when no batch ran (so non-bulk syncs stay quiet).
func (p *PhaseStats) Log(label string) {
batches := p.Batches.Load()
if batches == 0 {
return
}
prep := time.Duration(p.PrepNanos.Load())
scan := time.Duration(p.ScanNanos.Load())
write := time.Duration(p.WriteNanos.Load())
avg := float64(p.WriteBatchSize.Load()) / float64(batches)
log.Printf(
"%s phase totals: prep=%s scan=%s write=%s "+
"batches=%d avg_batch=%.1f sessions_written=%d",
label,
prep.Round(time.Millisecond),
scan.Round(time.Millisecond),
write.Round(time.Millisecond),
batches,
avg,
p.BatchedWrites.Load(),
)
}