Real-Time HOS Compliance Tracking for Trucking Fleets

Real-Time HOS Compliance Tracking for Trucking Fleets

Hours of Service (HOS) violations carry serious consequences for trucking fleets—fines, out-of-service orders, and accident liability. RisingWave, a PostgreSQL-compatible streaming database, can ingest ELD event streams and maintain continuously updated compliance views for every driver, firing alerts when drivers approach regulatory limits rather than after violations have already occurred.

Why HOS Compliance Tracking Matters for Trucking

Federal Motor Carrier Safety Administration (FMCSA) HOS regulations govern exactly how many hours commercial drivers can operate, take short breaks, and must rest before resuming duty. The core rules involve the 11-hour driving limit, the 14-hour on-duty window, mandatory 30-minute breaks, and the 60/70-hour weekly limits—and violations result in immediate enforcement consequences.

The challenge fleet managers face:

  • ELD data arrives continuously from Electronic Logging Devices across every truck in the fleet, but compliance dashboards typically refresh every few minutes or on demand.
  • Drivers approaching limits need warnings before they violate, not after their ELD auto-locks.
  • Dispatch decisions depend on compliance status—assigning a new load to a driver who has 45 minutes of drive time remaining wastes resources and creates liability.
  • Audit trails require accurate reconstruction of every duty status change, which demands durable, timestamped event storage.

Real-time HOS tracking using streaming SQL means compliance status is always current—updated within seconds of every duty-status change event from the ELD.

How Streaming SQL Solves This

RisingWave ingests ELD duty-status events from Kafka and maintains incremental materialized views that accumulate driving time, on-duty time, and break time per driver within each regulatory window. Compliance officers and dispatch systems query these views exactly like a PostgreSQL table, always getting the current state.

Key advantages:

  • Incremental computation: RisingWave updates only the affected rows when new events arrive, not recomputing from scratch
  • Multiple time windows: Simultaneously track the 8-hour, 11-hour, 14-hour, and 70-hour windows in separate views
  • Event sourcing: The raw ELD event stream serves as the immutable log; views are derived state
  • Sub-second latency: Compliance views reflect duty changes within seconds

Building the System

Step 1: Data Source

CREATE SOURCE eld_duty_events (
    event_id        VARCHAR,
    driver_id       VARCHAR,
    vin             VARCHAR,
    carrier_id      VARCHAR,
    event_time      TIMESTAMPTZ,
    duty_status     VARCHAR,  -- 'DRIVING','ON_DUTY_NOT_DRIVING','SLEEPER_BERTH','OFF_DUTY'
    odometer_miles  FLOAT,
    gps_lat         DOUBLE PRECISION,
    gps_lon         DOUBLE PRECISION,
    co_driver_id    VARCHAR
)
WITH (
    connector = 'kafka',
    topic = 'fleet.eld.events',
    properties.bootstrap.server = 'kafka:9092',
    scan.startup.mode = 'latest'
) FORMAT PLAIN ENCODE JSON;

Step 2: Real-Time View

Compute rolling driving and on-duty hours within the current 24-hour calendar day per driver. This view powers the real-time compliance dashboard.

CREATE MATERIALIZED VIEW driver_hos_daily AS
SELECT
    driver_id,
    window_start,
    window_end,
    SUM(CASE WHEN duty_status = 'DRIVING' THEN 1 ELSE 0 END)             AS driving_events,
    -- Approximate driving minutes: count events * assumed avg segment minutes
    -- In production, compute duration between consecutive status-change events
    COUNT(*) FILTER (WHERE duty_status IN ('DRIVING','ON_DUTY_NOT_DRIVING')) AS on_duty_events,
    COUNT(*) FILTER (WHERE duty_status = 'SLEEPER_BERTH')                  AS sleeper_events,
    COUNT(*) FILTER (WHERE duty_status = 'OFF_DUTY')                       AS off_duty_events,
    MAX(odometer_miles)                                                    AS max_odometer
FROM TUMBLE(
    eld_duty_events,
    event_time,
    INTERVAL '24 hours'
)
GROUP BY driver_id, window_start, window_end;

For a more precise view that computes actual hours from consecutive status changes, use a self-join pattern with window functions:

CREATE MATERIALIZED VIEW driver_driving_hours_8h AS
SELECT
    driver_id,
    window_start,
    window_end,
    COUNT(*) FILTER (WHERE duty_status = 'DRIVING') AS driving_segments,
    COUNT(*) FILTER (WHERE duty_status = 'OFF_DUTY' OR duty_status = 'SLEEPER_BERTH') AS rest_segments
FROM TUMBLE(
    eld_duty_events,
    event_time,
    INTERVAL '8 hours'
)
GROUP BY driver_id, window_start, window_end;

Track the 70-hour / 8-day rule with a wider window:

CREATE MATERIALIZED VIEW driver_hos_70h AS
SELECT
    driver_id,
    window_start,
    window_end,
    COUNT(*) FILTER (WHERE duty_status IN ('DRIVING','ON_DUTY_NOT_DRIVING')) AS on_duty_events_8day
FROM TUMBLE(
    eld_duty_events,
    event_time,
    INTERVAL '8 days'
)
GROUP BY driver_id, window_start, window_end;

Step 3: Alerts

Alert dispatchers and compliance officers when drivers approach the 11-hour driving threshold or the 14-hour on-duty window, giving time to arrange handoffs or plan stops.

CREATE MATERIALIZED VIEW hos_compliance_alerts AS
SELECT
    d.driver_id,
    d.window_start,
    d.window_end,
    d.driving_segments,
    d.rest_segments,
    CASE
        WHEN d.driving_segments >= 10 THEN 'APPROACHING_11H_LIMIT'
        WHEN d.driving_segments >= 11 THEN 'AT_DRIVING_LIMIT'
        WHEN d.rest_segments = 0 AND d.driving_segments >= 8 THEN 'NO_BREAK_TAKEN'
        ELSE 'COMPLIANT'
    END AS compliance_status
FROM driver_driving_hours_8h d
WHERE d.driving_segments >= 8 OR d.rest_segments = 0;

CREATE SINK hos_alerts_sink
FROM hos_compliance_alerts
WITH (
    connector = 'kafka',
    topic = 'fleet.hos.alerts',
    properties.bootstrap.server = 'kafka:9092'
) FORMAT PLAIN ENCODE JSON;

Comparison Table

ApproachAlert Lead TimeWindow AccuracyDispatch IntegrationAudit Trail
ELD dashboard (manual check)NonePoint-in-timeNoneYes (device)
Periodic polling (5 min)~5 minutesApproximateCustomPartial
Custom stream processorSecondsHighYesYes
RisingWaveSecondsHighYes (SQL/Kafka)Yes

FAQ

Q: How does RisingWave handle the complexity of different HOS rule sets (property-carrying vs. passenger-carrying)? You can parameterize your materialized view logic by joining ELD events against a driver reference table that includes rule set codes. Conditional logic in SQL (CASE WHEN rule_set = 'property' THEN 11 ELSE 10 END) applies the correct thresholds per driver.

Q: Can RisingWave reconstruct a driver's 7-day or 8-day duty history for DOT audits? Yes. The raw ELD event stream stored in Kafka or an S3-backed source provides the immutable audit log. RisingWave views can query historical windows by adjusting the time range in your aggregation predicates, and you can query the underlying source directly for point-in-time reconstruction.

Q: What happens to compliance views when a driver resets with a 34-hour restart? A 34-hour restart is itself an ELD duty-status event (OFF_DUTY for the required duration). Once the restart window closes, subsequent views naturally begin accumulating from fresh state. You can add a reset flag in your alert logic by checking whether the most recent off-duty block meets the restart duration requirement.

Key Takeaways

  • Real-time HOS monitoring prevents violations by alerting dispatchers before limits are reached, enabling proactive load planning.
  • RisingWave's windowed SQL aggregations over ELD event streams track multiple regulatory time windows simultaneously—8-hour, 11-hour, 14-hour, and 70-hour—within a single system.
  • Streaming SQL eliminates the polling loops and custom state management typically required to compute running HOS totals, reducing engineering complexity.
  • The Kafka-based event sourcing architecture provides the immutable audit trail required for DOT compliance while also powering real-time dashboards.

Best-in-Class Event Streaming
for Agents, Apps, and Analytics
GitHubXLinkedInSlackYouTube
Sign up for our to stay updated.