12 KiB
Superseded: This 2026-06-23 split design is historical context. Use
docs/target-architecture.mdanddocs/superpowers/specs/2026-06-29-vehicle-ingest-redesign.mdfor the current production architecture: TDengine is the default hot history store,sink-archiveowns raw bytes, andevent-file-storeis optional compatibility only.
GB32960 Service Split Design
Date: 2026-06-23
Goal
Split the current all-in-one vehicle ingest runtime into lower-coupled, higher-cohesion services. The first production slice focuses on GB/T 32960 because it is the active high-pressure path: persistent TCP connections, protocol ACKs, raw frame volume, history queries, and downstream analytics are currently assembled in one bootstrap-all process.
The target outcome is:
- Keep GB32960 ingest reliable and fast.
- Move disk writes and history queries out of the protocol process.
- Move statistics and analysis out of the protocol process.
- Use Kafka as the durable boundary between ingest, history, and analytics.
- Preserve
bootstrap-allduring migration as a local development and rollback entry point.
Decision
Use ACK semantics A:
A GB32960 frame is ACKed successfully after the protocol service parses/accepts it and writes the required Kafka message(s) successfully.
The protocol service must not wait for raw archive, DuckDB/Parquet writes, snapshot generation, or statistics computation before sending the GB32960 response frame.
Service Boundaries
gb32960-ingest-app
Purpose: own GB32960 connection handling and Kafka production.
Responsibilities:
- Listen on the GB32960 TCP port.
- Handle platform login, VIN authorization, TLS if enabled, and session state required for protocol responses.
- Decode GB32960 frames and apply vendor extension parsing.
- Produce raw frame records and normalized event records to Kafka.
- ACK successful reports only after Kafka production succeeds.
- Publish malformed or rejected frames to a DLQ when possible.
- Expose operational health and metrics for connections, decode rate, Kafka latency, ACK latency, and DLQ count.
Non-responsibilities:
- No local raw archive writes.
- No DuckDB or Parquet history writes.
- No snapshot query API.
- No vehicle statistics or analysis.
- No long-running analytical calculations.
Initial module dependencies:
ingest-apiingest-coreingest-codec-commonsession-coreprotocol-gb32960sink-kafkaobservability
vehicle-history-app
Purpose: own raw storage, historical index storage, and history query APIs.
Responsibilities:
- Consume GB32960 raw records and normalized event records from Kafka.
- Write raw
.binpayloads throughsink-archive. - Write queryable event indexes through
event-file-store. - Provide APIs for raw frame lookup, decoded frame lookup, history query, and snapshot/source-frame lookup.
- Rebuild or backfill the DuckDB sidecar index from stored Parquet files when needed.
- Track consumer lag, write latency, archive failures, index failures, and query latency.
Non-responsibilities:
- No TCP protocol listener.
- No GB32960 ACK decisions.
- No online statistics computation.
- No direct coupling to
gb32960-ingest-appinternals.
Initial module dependencies:
ingest-apisink-kafkasink-archiveevent-file-storeevent-history-serviceprotocol-gb32960only for raw-frame decode/query supportobservability
vehicle-analytics-app
Purpose: own vehicle state, daily statistics, alarms, and later analytics.
Responsibilities:
- Consume normalized vehicle event records from Kafka.
- Maintain latest vehicle state if enabled.
- Compute daily vehicle statistics and alarm-derived metrics.
- Persist analytical outputs to the selected backend.
- Support reprocessing from Kafka offsets when analytical logic changes.
- Track consumer lag, per-VIN processing latency, aggregation failures, and output write latency.
Non-responsibilities:
- No protocol listener.
- No GB32960 ACK decisions.
- No raw archive writes.
- No normal-path dependency on raw archive or history APIs.
Initial module dependencies:
ingest-apisink-kafkavehicle-state-servicevehicle-stat-serviceobservability
Kafka Contract
Kafka is the service boundary. Messages must be stable enough that vehicle-history-app and vehicle-analytics-app do not depend on protocol service internals.
Topics
Use versioned topics:
vehicle.raw.gb32960.v1vehicle.event.gb32960.v1vehicle.dlq.gb32960.v1
The current topic names under lingniu.ingest.sink.kafka.topics can remain temporarily for compatibility, but the split apps should converge on versioned topic names.
Keys
Use VIN as the Kafka key whenever a VIN is available.
Benefits:
- Preserves per-vehicle ordering within a partition.
- Lets history and analytics scale by partition.
- Keeps stateful analytics simpler.
When VIN is unavailable, use a stable fallback key such as platform account plus peer address plus receive time bucket.
Raw Record
Topic: vehicle.raw.gb32960.v1
Minimum fields:
schemaVersionprotocol = GB32960eventIdreceiveTimeeventTimeif decodedpeerplatformAccountvinif decodedcommandprotocolVersionrawByteschecksumStatusdecodeStatusmetadata
Consumers:
vehicle-history-appwrites archive.binfiles and raw-frame query indexes.- Future replay tools can regenerate normalized events from raw records.
Normalized Event Record
Topic: vehicle.event.gb32960.v1
Minimum fields:
schemaVersionprotocol = GB32960eventIdsourceRawEventIdreceiveTimeeventTimeplatformAccountvincommandeventTypenormalizedFieldsvendorFieldsalarmFieldsmetadata
Consumers:
vehicle-history-appwrites queryable history records.vehicle-analytics-appupdates state and statistics.
DLQ Record
Topic: vehicle.dlq.gb32960.v1
Minimum fields:
schemaVersionstageerrorCodeerrorMessagereceiveTimepeerplatformAccountif knownvinif knownrawByteswhen availablemetadata
Typical stages:
FRAME_DECODEBODY_PARSEAUTHKAFKA_PRODUCEACK_WRITE
ACK and Failure Semantics
Success Path
- Receive TCP bytes.
- Decode a complete GB32960 frame.
- Validate checksum/auth/session rules.
- Build raw and normalized Kafka records.
- Produce required Kafka records successfully.
- Send GB32960 success ACK.
Kafka Produce Failure
If required Kafka production fails, the service must not send a success ACK.
Allowed behavior:
- Retry within a bounded timeout.
- Return a GB32960 failure response when the protocol allows it.
- Close the channel after repeated produce failures.
- Emit local error logs and metrics.
Do not silently ACK frames that have not crossed the Kafka durability boundary.
History or Analytics Failure
Failures in vehicle-history-app or vehicle-analytics-app must not affect GB32960 ACKs. They are handled by Kafka offset retry, DLQ, operational alerting, and backfill/replay.
Current Coupling to Remove
bootstrap-all currently assembles protocol modules, Kafka producer/consumer, archive sink, event file store, history API, vehicle state, vehicle statistics, command gateway, and multiple inbound protocols in one application.
The split should remove these production couplings:
- Protocol runtime directly containing archive and event-file-store writers.
- Protocol runtime directly containing statistics processors.
- History query API sharing the same process as TCP ingest.
- Kafka consumer workers living in the same all-in-one app as protocol listeners.
- Operational scaling tied to one JVM for ingest, storage, queries, and analytics.
Migration Plan
Phase 1: Add Split App Entrypoints
Add new app modules:
modules/apps/gb32960-ingest-appmodules/apps/vehicle-history-appmodules/apps/vehicle-analytics-app
Keep modules/apps/bootstrap-all unchanged as the fallback runtime.
Phase 2: GB32960 Ingest App
Create a production profile for gb32960-ingest-app:
- Enable
protocol-gb32960. - Enable Kafka producer.
- Disable local archive.
- Disable event-file-store.
- Disable history API.
- Disable vehicle state/stat services.
- Disable unrelated protocols.
Acceptance:
- GB32960 TCP port starts.
- Platform login works.
- Realtime report frames are parsed.
- Kafka records are produced with VIN keys.
- ACK is sent only after Kafka success.
Phase 3: History App
Create vehicle-history-app:
- Enable Kafka consumer.
- Enable archive sink.
- Enable event-file-store.
- Enable event-history HTTP API.
- Disable protocol listeners.
- Disable analytics processors.
Acceptance:
- Consumes
vehicle.raw.gb32960.v1. - Writes raw
.binfiles. - Consumes
vehicle.event.gb32960.v1. - Writes Parquet/DuckDB indexes.
- Queries can locate raw frames and decoded snapshots.
Phase 4: Analytics App
Create vehicle-analytics-app:
- Enable Kafka consumer.
- Enable vehicle state if a state backend is configured.
- Enable vehicle statistics.
- Disable protocol listeners.
- Disable archive and event-file-store.
Acceptance:
- Consumes
vehicle.event.gb32960.v1. - Maintains expected per-VIN state/stat outputs.
- Can be restarted and resume from Kafka offsets.
- Does not affect ingest ACK latency.
Phase 5: Parallel Run and Cutover
Run old and new paths in parallel for a bounded validation window.
Compare:
- Received frame count.
- Kafka produced record count.
- Raw archive file count.
- Unique VIN count.
- History query count and sample query correctness.
- Analytics output count and sample values.
- GB32960 ACK latency before and after split.
Cut over only after counts and sample queries match within the agreed tolerance.
Operational Guidance
Scale services independently:
- Scale
gb32960-ingest-appby connection count and Kafka produce latency. - Scale
vehicle-history-appby disk throughput, consumer lag, and query latency. - Scale
vehicle-analytics-appby consumer lag and computation latency.
Monitor:
- Kafka producer error rate and p99 produce latency.
- ACK p99 latency.
- TCP active connections.
- Decode failures and DLQ counts.
- Consumer lag per group.
- Archive write latency and failure rate.
- Event-file-store flush latency and failure rate.
- Analytics processing latency.
Risks and Mitigations
Risk: Kafka outage blocks GB32960 ACKs.
Mitigation: bounded producer retry, clear failure ACK/close behavior, Kafka cluster monitoring, and optional local emergency spool only as a later explicit design.
Risk: Two Kafka topics for raw and normalized events can diverge.
Mitigation: include eventId and sourceRawEventId, produce records transactionally if required, or publish a single envelope containing both raw and normalized sections in the first implementation if transaction support is not ready.
Risk: History app reprocessing duplicates archive/index records.
Mitigation: deterministic archive keys and idempotent index writes keyed by eventId or rawArchiveUri.
Risk: Analytics logic changes require backfill.
Mitigation: keep raw and normalized topics with sufficient retention; allow analytics consumers to reset offsets or run a backfill group.
Risk: Query APIs accidentally depend on ingest internals.
Mitigation: history APIs should depend on event-file-store, sink-archive, and decode libraries only, not gb32960-ingest-app.
Open Decisions
These are intentionally left for implementation planning:
- Whether Kafka production uses two independent sends or a transaction for raw + normalized records.
- Exact protobuf/JSON schema shape for
vehicle.raw.gb32960.v1andvehicle.event.gb32960.v1. - Whether latest snapshot state belongs only to
vehicle-analytics-appor is also materialized byvehicle-history-appfor query convenience. - Initial Kafka partition count and retention duration.
- Whether command downlink stays with
command-gatewayor gets a separate command service later.
Completion Criteria
The split is complete when:
gb32960-ingest-app,vehicle-history-app, andvehicle-analytics-appare separate runnable app modules.gb32960-ingest-appcan receive live GB32960 data and ACK after Kafka success.vehicle-history-appcan consume Kafka and provide raw/history/snapshot query APIs.vehicle-analytics-appcan consume Kafka and compute state/stat outputs.bootstrap-allremains available for development and rollback.- Verification covers build, app startup, Kafka production/consumption, raw archive writes, history queries, analytics outputs, and ACK latency.