perf(go): decouple realtime mysql projection

This commit is contained in:
lingniu
2026-07-03 11:49:13 +08:00
parent 6fb6262d0d
commit 76f5a20b79
3 changed files with 136 additions and 17 deletions

View File

@@ -84,7 +84,16 @@ func main() {
logger.Error("realtime snapshot mysql schema bootstrap failed", "error", err)
os.Exit(1)
}
updater = compositeRealtimeUpdater{primary: repository, secondary: snapshotWriter}
mysqlUpdater := realtimeUpdater(snapshotWriter)
if env("MYSQL_REALTIME_ASYNC_ENABLED", "true") != "false" {
mysqlUpdater = newAsyncSecondaryRealtimeUpdater(
nil,
snapshotWriter,
envInt("MYSQL_REALTIME_ASYNC_QUEUE_SIZE", 20000),
envInt("MYSQL_REALTIME_ASYNC_WORKERS", 4),
)
}
updater = compositeRealtimeUpdater{primary: repository, secondary: mysqlUpdater}
logger.Info("realtime mysql snapshot enabled", "table", "vehicle_realtime_snapshot", "plate_binding_table", bindingTable, "plate_cache_ttl_seconds", envInt("PLATE_CACHE_TTL_SECONDS", 600))
}
mux.Handle("/api/stats/daily-metrics", stats.NewMetricHandler(stats.NewMetricRepository(db)))
@@ -222,6 +231,69 @@ func (u compositeRealtimeUpdater) Update(ctx context.Context, env envelope.Frame
return u.secondary.Update(ctx, env)
}
type asyncSecondaryRealtimeUpdater struct {
primary realtimeUpdater
secondary realtimeUpdater
queue chan envelope.FrameEnvelope
cancel context.CancelFunc
}
func newAsyncSecondaryRealtimeUpdater(primary realtimeUpdater, secondary realtimeUpdater, queueSize int, workers int) *asyncSecondaryRealtimeUpdater {
if queueSize <= 0 {
queueSize = 20000
}
if workers <= 0 {
workers = 4
}
ctx, cancel := context.WithCancel(context.Background())
updater := &asyncSecondaryRealtimeUpdater{
primary: primary,
secondary: secondary,
queue: make(chan envelope.FrameEnvelope, queueSize),
cancel: cancel,
}
for i := 0; i < workers; i++ {
go updater.run(ctx)
}
return updater
}
func (u *asyncSecondaryRealtimeUpdater) Update(ctx context.Context, env envelope.FrameEnvelope) error {
if u.primary != nil {
if err := u.primary.Update(ctx, env); err != nil {
return err
}
}
if u.secondary == nil {
return nil
}
select {
case u.queue <- env:
return nil
default:
return nil
}
}
func (u *asyncSecondaryRealtimeUpdater) run(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
case env := <-u.queue:
secondaryCtx, cancel := context.WithTimeout(context.Background(), kafkaMessageOperationTimeout)
_ = u.secondary.Update(secondaryCtx, env)
cancel()
}
}
}
func (u *asyncSecondaryRealtimeUpdater) Close() {
if u.cancel != nil {
u.cancel()
}
}
type kafkaMessageCommitter interface {
CommitMessages(context.Context, ...kafka.Message) error
}