Skip to content

fix: refuse a report with sequence_id 0, serve more than 100 dApps and echo any request id - #126

Merged
Thecave3 merged 2 commits into
mainfrom
fix/report-sequence-id-and-id-ranges
Oct 12, 2026
Merged

Thecave3 merged 2 commits into
mainfrom
fix/report-sequence-id-and-id-ranges

Conversation

@Thecave3

Copy link
Copy Markdown
Collaborator

Summary

  • A report with sequence_id 0 is refused at the API. E3-SequenceID is INTEGER (1..4294967295) and mandatory on a DAppReport. A dApp that passed 0 queued the report without any error and the outbound thread then failed the APER encode of every one of them (APER encoding failed for type: E3-SequenceID, one log line per report, millions in a long run), and no report left the dApp. send_report now returns INVALID_PARAM and logs why. A control still accepts 0: its sequenceId is optional and 0 means absent.
  • dApp and subscription ids run 1 to 65535, lowest free first. Both were handed out from 1 to 100 and wrapped, so the 101st dApp got an id that was already live and a subscription id could take over one still in use. A dApp that reconnects now gets the same id back, and when no id is left it gets a well-formed negative reply.
  • A setup request id is echoed unless it is 0. Ids above 1000 were replaced with a fresh one in the response, although a peer that counts across message types passes 1000 within seconds. E3-MessageID is 1..4294967295.
  • Python DAppSession.subscribe returns int64, as send_control and send_report already do, so a request id above 2^31 is not read as an error code.

No wire change: the grammar already allowed these ranges. VERSION is not bumped here; the 0.2.4 release commit follows the merge.

The id allocation overlaps with #123 (which also carries the ASN.1 string and bounds changes); the open stack is rebased on top of this in a separate flow.

Type of change

  • Bug fix
  • New feature / enhancement
  • New Service Model
  • Refactor (no behavior change)
  • Documentation
  • Test / CI / packaging
  • Other (explain):

Linked issue

Fixes #99.

Testing

Debug build with ASN.1, JSON, Protocol Buffers and SWIG, ctest: 25/25 pass. New tests: lowest free dApp id and reconnect, 300 dApps with distinct dApp and subscription ids, reuse of a deleted subscription id, a setup request with message id 3000000000 echoed, and a report with sequence_id 0 refused while a control with 0 is not.

Twin-repo coordination

The dApp library change that stops sending 0 (it assigns report ids itself) is a separate PR and works with 0.2.3 as well. OAI is unaffected.

…y encode

E3-SequenceID is INTEGER (1..4294967295) and mandatory on a DAppReport. A
dApp that passed 0 queued the report without any error, and the outbound
thread then failed the APER encode for every one of them ("APER encoding
failed for type: E3-SequenceID"), one log line per report and no report on
the wire.

send_report now returns INVALID_PARAM for a sequence_id of 0 and logs why, so
the caller sees it at the call. A control keeps accepting 0: its sequenceId is
optional and 0 means absent.

Assisted-by: Claude:claude-opus-5-5
The grammar allows dApp and subscription ids up to 65535 and message ids up to
4294967295, but the RAN agent still stopped at the old limits:

- dApp ids and subscription ids were handed out from 1 to 100 and wrapped, so
  the 101st dApp got an id that was already live, and a subscription id could
  silently take over one that was still in use. Both now take the lowest free
  id in 1..65535, so a dApp that reconnects gets the same id back, and when no
  id is left the dApp gets a well-formed negative reply.
- A setup request id above 1000 was replaced by a fresh one in the response,
  although a peer that counts across message types passes 1000 within
  seconds. Every non-zero id is now echoed; only 0, which cannot be encoded,
  is replaced.
- DAppSession.subscribe returns int64, as send_control and send_report
  already do, so a request id above 2^31 is not read as an error code.

Tests: the allocator (lowest free id, reconnect, 300 dApps with distinct dApp
and subscription ids, reuse of a deleted subscription id) and a setup request
with a message id of 3000000000.

Assisted-by: Claude:claude-opus-5-5
@github-actions

Copy link
Copy Markdown
Contributor

CI report — d053920 — ✅ all checks passed

Workflow Result Time Run
Commit policy ✅ success 1m51s #137
E2E dApp Integration ✅ success 1m58s #141
E2E Topologies (multi-dApp / multi-RAN) ✅ success 2m35s #139
Full-loop Latency Benchmark ✅ success 0m52s #140
Unit Tests ✅ success 4m33s #167
latrec portability ⏭️ not triggered (paths filter) — —
MPMC Queue Benchmark ⏭️ not triggered (paths filter) — —
E2E Topologies (multi-dApp / multi-RAN)

zmq/ipc

  • ✅ 1 RAN - 1 dApp: indications=5
    • dapp peer=t11 ran=ran-solo sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
  • ✅ 1 RAN - 2 dApps: dApp#1 ind=5 sub=1, dApp#2 ind=6 sub=2, RAN saw 2 dApps
    • dapp1 peer=t12 ran=ran-shared sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=1 max=1 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
    • dapp2 peer=t12 ran=ran-shared sub=2 indications=6 seq=[0..5] dropped=0 (0%) age_ms(avg=1 max=1 @seq=0) hist[<=1:6 2-5:0 6-10:0 >10:0]
  • ✅ 2 RANs - 1 dApp: from ran-a ind=5, from ran-b ind=5
    • dapp peer=t2a ran=ran-a sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
    • dapp peer=t2b ran=ran-b sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]

zmq/tcp

  • ✅ 1 RAN - 1 dApp: indications=5
    • dapp peer=default ran=ran-solo sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
  • ✅ 1 RAN - 2 dApps: dApp#1 ind=5 sub=1, dApp#2 ind=6 sub=2, RAN saw 2 dApps
    • dapp1 peer=default ran=ran-shared sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
    • dapp2 peer=default ran=ran-shared sub=2 indications=6 seq=[0..5] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:6 2-5:0 6-10:0 >10:0]
  • ✅ 2 RANs - 1 dApp: from ran-a ind=5, from ran-b ind=5
    • dapp peer=default ran=ran-a sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
    • dapp peer=off100 ran=ran-b sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
E2E dApp Integration

✅ posix/ipc

  • dApp exit: 0
  • Indications received: 7

✅ posix/tcp

  • dApp exit: 0
  • Indications received: 7

✅ zmq/ipc

  • dApp exit: 0
  • Indications received: 7

✅ zmq/tcp

  • dApp exit: 0
  • Indications received: 7
Full-loop Latency Benchmark

Full-loop latency

Full-loop latency benchmark (N=1081 after 50 warmup)

All values in microseconds (μs). Link: zmq, transport: ipc, encoding: ASN.1 APER.

# Description Tags mean p50 p99 max
1 Collect indication data RECORD_BEGIN to ENCODE_E3SM_BEGIN 0.05 0.05 0.08 0.17
2 Create & encode indication ENCODE_E3SM_BEGIN to ENCODE_E3SM_DONE 0.29 0.26 0.59 0.72
3 Encode E3AP (indication) EMIT_ENTER to ENQUEUE, then DEQUEUE to ENCODE_E3AP_DONE 1.22 1.14 2.57 9.04
4 Queuing (indication) ENQUEUE to DEQUEUE 1.89 1.76 4.75 38.32
5 Delivery (indication) ENCODE_E3AP_DONE to SEND_DONE 0.11 0.09 0.32 2.44
6 E3 wire (RAN -> dApp) SEND_DONE to RECV 28.23 28.31 35.90 42.78
7 Decode E3AP (indication) RECV to DECODE_E3AP_DONE 0.65 0.63 1.16 6.76
8 libe3 dispatch (indication) DECODE_E3AP_DONE to DELIVER_BEGIN 0.04 0.04 0.07 0.25
9 Decode indication DELIVER_BEGIN to DECODE_E3SM_DONE 0.23 0.19 0.39 0.71
10 Process data DECODE_E3SM_DONE to ENCODE_E3SM_BEGIN 0.03 0.04 0.05 0.09
11 Create & encode control ENCODE_E3SM_BEGIN to ENCODE_E3SM_DONE 0.10 0.09 0.24 0.35
12 Encode E3AP (control) EMIT_ENTER to ENQUEUE, then DEQUEUE to ENCODE_E3AP_DONE 1.14 1.16 1.75 2.71
13 Queuing (control) ENQUEUE to DEQUEUE 8.76 8.97 10.50 17.55
14 Delivery (control) ENCODE_E3AP_DONE to SEND_DONE 2.98 3.02 4.71 11.12
15 E3 wire (dApp -> RAN) SEND_DONE to RECV 28.84 28.74 35.12 46.16
16 Decode E3AP (control) RECV to DECODE_E3AP_DONE 0.82 0.78 2.52 15.97
17 libe3 dispatch (control) DECODE_E3AP_DONE to DECODE_E3SM_BEGIN 0.06 0.06 0.16 0.20
18 Decode & handle control DECODE_E3SM_BEGIN to DECODE_E3SM_DONE 0.16 0.16 0.31 0.50
Total Total round-trip 75.79 75.11 87.31 114.62

ubuntu-latest, Release build, ZMQ + IPC, ASN.1 APER.

These numbers are measured inside a GitHub Actions container and should be treated as an upper bound on E3AP's and the library's own latency, not a representative deployment measurement.

Ready to merge (fast-forward only)

A maintainer can land the reviewed commits with:

git fetch origin
git checkout main && git merge --ff-only d053920f871011deb67a023cc19046ca6083f80e && git push origin main

Head: d053920f871011deb67a023cc19046ca6083f80e (branch fix/report-sequence-id-and-id-ranges). If --ff-only fails as non-fast-forward, the branch must be rebased on the latest main.

One comment per PR, rewritten in place once every workflow for d053920 finished.

@Thecave3
Thecave3 merged commit d053920 into main Oct 12, 2026
25 checks passed
@Thecave3
Thecave3 deleted the fix/report-sequence-id-and-id-ranges branch October 12, 2026 01:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Interop]: Identifier ranges: libe3 caps dApp and subscription ids at 100 and still limits request ids to 1000

1 participant