Trax makes aircraft maintenance a change-data-capture problem
A production Quarkus architecture turns Oracle changes into targeted WebSocket and mobile notifications without polling the system of record.
Trax has described a production architecture that uses Quarkus to deliver near-real-time aircraft-maintenance updates from an existing Oracle system to React dashboards and mechanics’ iPads. The useful part is not the industry setting alone. It is the way the design treats change-data capture, fan-out and client invalidation as separate problems rather than trying to stream database rows directly into user interfaces.
The database is the event source
In the Quarkus engineering account, Trax says its Oracle ERP database is written by multiple applications, batch jobs and integrations. That rules out emitting events only from the new service after its own transactions commit. Debezium instead tails Oracle’s redo log and publishes changes to Kafka, where Quarkus Kafka Streams handlers update caches and decide which client state has become stale.
The messages sent to clients are intentionally small. Rather than replicate database records over WebSockets, the service identifies the affected domain—such as a work order or aircraft—and lets the React or Swift client refetch the relevant REST resource. That makes the real-time channel an invalidation mechanism, while the HTTP API remains the source of the current representation.
Broadcast semantics require per-pod identity
One operational detail deserves attention from Kubernetes teams. Kafka Streams normally divides partitions among members that share an application ID. That behavior is correct for work processing but wrong when every pod owns a different set of live WebSocket connections: if replicas shared a consumer group, only one replica would receive a given change and users connected elsewhere could miss the notification.
Trax therefore includes the pod’s HOSTNAME in the Kafka Streams application ID. Each pod receives every record in its own consumer group and can notify its local connections. The post argues that the pod name offers two useful properties: it is unique across replicas and remains stable when a container restarts inside the same pod. A newly generated UUID would provide uniqueness but strand committed offsets and state behind a new identity after every restart.
Debouncing protects the clients
Change-data capture can turn one user action or batch job into many row-level events. Trax says forwarding each event caused unnecessary React rendering and mobile battery use. Its service coalesces pending updates by natural record identity and uses a shared one-second trailing timer, so repeated changes to the same work order collapse into one notification and a burst across tables can become one multi-type message.
Foreground clients receive those messages through Quarkus WebSockets Next. Backgrounded iPads use Apple Push Notification Service, triggered asynchronously through CDI events. The same endpoint also handles browser text-message keepalives and native WebSocket ping frames, reflecting the different capabilities of JavaScript and native clients.
Keep the contract in the build
The other reusable pattern is contract generation. Quarkus SmallRye OpenAPI writes the schema into the source tree as a build artifact. Trax generates a Swift client and React Query hooks from that same file, making an incompatible Java API change visible in client builds rather than after deployment.
Teams adopting this shape should test the delivery assumptions, not just the happy path: at-least-once Kafka processing, per-pod consumer-group growth, reconnect behavior, notification coalescing and stale-client recovery all become part of the application contract. The case study’s strongest lesson is that “real time” is less about pushing more data than about choosing the smallest reliable signal that tells each client what to refresh.
sources
comments · 0