This commit is contained in:
2026-05-25 11:00:13 +07:00
parent c5e980aa52
commit 81ba67f346
12 changed files with 1534 additions and 77 deletions

View File

@@ -25,32 +25,39 @@ type Producer interface {
// IngestService is the core pipeline: validate → ratelimit → timestamp normalize
// → late-check → dedup → flatten → schema-conflict → push Kafka.
type IngestService struct {
producer Producer
limiter ratelimit.Limiter
dedup dedup.Dedup
schema repo.SchemaRepo
log *zap.Logger
lateAfter time.Duration
producer Producer
limiter ratelimit.Limiter
dedup dedup.Dedup
schema repo.SchemaRepo
log *zap.Logger
lateAfter time.Duration
rateLimitRPS int // 0 = unlimited
}
// IngestDeps groups dependencies for cleaner construction.
type IngestDeps struct {
Producer Producer
Limiter ratelimit.Limiter
Dedup dedup.Dedup
Schema repo.SchemaRepo
Log *zap.Logger
LateAfter time.Duration
Producer Producer
Limiter ratelimit.Limiter
Dedup dedup.Dedup
Schema repo.SchemaRepo
Log *zap.Logger
LateAfter time.Duration
RateLimitRPS int
}
func NewIngestService(d IngestDeps) *IngestService {
rps := d.RateLimitRPS
if rps < 0 {
rps = 0
}
return &IngestService{
producer: d.Producer,
limiter: d.Limiter,
dedup: d.Dedup,
schema: d.Schema,
log: d.Log,
lateAfter: d.LateAfter,
producer: d.Producer,
limiter: d.Limiter,
dedup: d.Dedup,
schema: d.Schema,
log: d.Log,
lateAfter: d.LateAfter,
rateLimitRPS: rps,
}
}
@@ -67,14 +74,17 @@ type IngestContext struct {
func (s *IngestService) Ingest(ctx context.Context, ictx IngestContext, raw *model.RawEvent) error {
now := time.Now().UTC()
// 3. rate limit per workspace
dec, err := s.limiter.Allow(ctx, ictx.WorkspaceID, defaultTierLimit, time.Second)
if err != nil {
return apperr.Internal(err)
}
if !dec.Allowed {
retry := (dec.RetryAfterMS / 1000) + 1
return apperr.TooManyRequests(retry)
// 3. rate limit per workspace (skip when RateLimitRPS == 0 -- intended
// for load testing only; do not run unbounded in production).
if s.rateLimitRPS > 0 {
dec, err := s.limiter.Allow(ctx, ictx.WorkspaceID, s.rateLimitRPS, time.Second)
if err != nil {
return apperr.Internal(err)
}
if !dec.Allowed {
retry := (dec.RetryAfterMS / 1000) + 1
return apperr.TooManyRequests(retry)
}
}
// 4-5. timestamps + late-event check
@@ -163,8 +173,6 @@ func (s *IngestService) IngestBatch(ctx context.Context, ictx IngestContext, bat
// helpers
// ---------------------------------------------------------------------------
const defaultTierLimit = 100 // rps; per-tier override comes from workspace.tier later
func derefTime(p *time.Time, fallback time.Time) time.Time {
if p == nil || p.IsZero() {
return fallback