feat(go): add nats fast writer
This commit is contained in:
@@ -40,7 +40,7 @@ type Config struct {
|
||||
|
||||
func (c Config) ttl() time.Duration {
|
||||
if c.OnlineTTL <= 0 {
|
||||
return 10 * time.Minute
|
||||
return time.Minute
|
||||
}
|
||||
return c.OnlineTTL
|
||||
}
|
||||
|
||||
@@ -27,6 +27,21 @@ func NewRepository(client *redis.Client, cfg Config) *Repository {
|
||||
return &Repository{client: client, cfg: cfg}
|
||||
}
|
||||
|
||||
func (r *Repository) FastUpdate(ctx context.Context, env envelope.FrameEnvelope) error {
|
||||
vin := strings.TrimSpace(env.VIN)
|
||||
if vin == "" {
|
||||
return nil
|
||||
}
|
||||
vehicleKey := strings.TrimSpace(env.VehicleKey())
|
||||
if vehicleKey == "" || strings.HasSuffix(vehicleKey, ":unknown") {
|
||||
return nil
|
||||
}
|
||||
if err := r.setKV(ctx, vin, env, env.Parsed); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.setOnline(ctx, vehicleKey, vin, []envelope.Protocol{env.Protocol}, env.ReceivedAtMS)
|
||||
}
|
||||
|
||||
func (r *Repository) Update(ctx context.Context, env envelope.FrameEnvelope) error {
|
||||
vin := strings.TrimSpace(env.VIN)
|
||||
if vin == "" {
|
||||
@@ -124,7 +139,7 @@ func (r *Repository) Update(ctx context.Context, env envelope.FrameEnvelope) err
|
||||
Protocols: protocols,
|
||||
TTLSeconds: int64(r.cfg.ttl().Seconds()),
|
||||
}
|
||||
if err := r.setJSON(ctx, onlineKey(vehicleKey), online, r.cfg.ttl()); err != nil {
|
||||
if err := r.setOnlineStatus(ctx, online); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.client.ZAdd(ctx, "vehicle:last_seen", redis.Z{Score: float64(env.ReceivedAtMS), Member: vehicleKey}).Err()
|
||||
@@ -209,12 +224,30 @@ func (r *Repository) setKV(ctx context.Context, vin string, env envelope.FrameEn
|
||||
pipe := r.client.Pipeline()
|
||||
for key, values := range grouped {
|
||||
pipe.HSet(ctx, key, values)
|
||||
pipe.Expire(ctx, key, r.cfg.ttl())
|
||||
}
|
||||
_, err := pipe.Exec(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Repository) setOnline(ctx context.Context, vehicleKey string, vin string, protocols []envelope.Protocol, lastSeenMS int64) error {
|
||||
online := OnlineStatus{
|
||||
VehicleKey: vehicleKey,
|
||||
VIN: vin,
|
||||
Online: true,
|
||||
LastSeenMS: lastSeenMS,
|
||||
Protocols: protocols,
|
||||
TTLSeconds: int64(r.cfg.ttl().Seconds()),
|
||||
}
|
||||
if err := r.setOnlineStatus(ctx, online); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.client.ZAdd(ctx, "vehicle:last_seen", redis.Z{Score: float64(lastSeenMS), Member: vehicleKey}).Err()
|
||||
}
|
||||
|
||||
func (r *Repository) setOnlineStatus(ctx context.Context, online OnlineStatus) error {
|
||||
return r.setJSON(ctx, onlineKey(online.VehicleKey), online, r.cfg.ttl())
|
||||
}
|
||||
|
||||
func (r *Repository) addProtocol(ctx context.Context, vehicleKey string, protocol envelope.Protocol) ([]envelope.Protocol, error) {
|
||||
key := protocolsKey(vehicleKey)
|
||||
if err := r.client.SAdd(ctx, key, string(protocol)).Err(); err != nil {
|
||||
|
||||
@@ -308,8 +308,45 @@ func TestRepositoryWritesRealtimeKVHashes(t *testing.T) {
|
||||
if stackKV["stack_water_outlet_temp_c"] != "63" {
|
||||
t.Fatalf("stack kv = %#v", stackKV)
|
||||
}
|
||||
if ttl := repo.client.TTL(ctx, "vehicle:rt-kv:GB32960:VIN001:vehicle").Val(); ttl <= 0 {
|
||||
t.Fatalf("kv ttl should be set, got %v", ttl)
|
||||
if ttl := repo.client.TTL(ctx, "vehicle:rt-kv:GB32960:VIN001:vehicle").Val(); ttl != -1 {
|
||||
t.Fatalf("kv ttl should not expire, got %v", ttl)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryFastUpdateOnlyWritesPermanentKVAndMinuteOnline(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
ctx := context.Background()
|
||||
|
||||
if err := repo.FastUpdate(ctx, envelope.FrameEnvelope{
|
||||
Protocol: envelope.ProtocolGB32960,
|
||||
VIN: "VIN001",
|
||||
EventTimeMS: 1000,
|
||||
ReceivedAtMS: 1100,
|
||||
Parsed: map[string]any{
|
||||
"data_units": []any{
|
||||
map[string]any{"type": "0x01", "name": "vehicle", "value": map[string]any{"soc_percent": 88.0}},
|
||||
},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("FastUpdate() error = %v", err)
|
||||
}
|
||||
|
||||
vehicleKV, err := repo.client.HGetAll(ctx, "vehicle:rt-kv:GB32960:VIN001:vehicle").Result()
|
||||
if err != nil {
|
||||
t.Fatalf("vehicle kv HGetAll error = %v", err)
|
||||
}
|
||||
if vehicleKV["soc_percent"] != "88" || vehicleKV["_event_time_ms"] != "1000" {
|
||||
t.Fatalf("vehicle kv = %#v", vehicleKV)
|
||||
}
|
||||
if ttl := repo.client.TTL(ctx, "vehicle:rt-kv:GB32960:VIN001:vehicle").Val(); ttl != -1 {
|
||||
t.Fatalf("kv ttl should not expire, got %v", ttl)
|
||||
}
|
||||
if ttl := repo.client.TTL(ctx, "vehicle:online:VIN001").Val(); ttl <= 0 || ttl > time.Minute {
|
||||
t.Fatalf("online ttl = %v, want within 1 minute", ttl)
|
||||
}
|
||||
if repo.client.Exists(ctx, "vehicle:latest:VIN001").Val() != 0 {
|
||||
t.Fatal("fast update should not write merged snapshot")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user