Diameter Credit-Control Server — Spring Boot 3.5 / Java 21 implementation of the Gy interface (RFC 4006, Application-Id 4) on jdiameter 1.7.x. Real-time prepaid mobile billing with idempotent partial debits, Postgres credit ledger, bundled PDN-Gateway test client, Testcontainers integration suite, Prometheus + Grafana observability.
Live dashboard recorded against the running stack — bundled PGW client looping CCR-I → CCR-U → CCR-T at ~1 cycle/2 s. Server: OPEN, all responses Result-Code 2001, p99 well under the 50 ms SLO line. Reproducible via
docker compose up && ./mvnw -pl server spring-boot:runplus the bundleddiameter-test-client --loop.
diameter-cc implements the Diameter Gy credit-control flow end-to-end:
- Peer state machine (CER/CEA, DWR/DWA, DPR/DPA) on TCP :3868
- CCR-Initial / Update / Terminate handling (RFC 4006 §6)
- Idempotent partial debits keyed on
(Session-Id, CC-Request-Number) - Postgres-backed credit account ledger with immutable audit trail
- Bundled
diameter-test-clientsimulating a PDN Gateway running a 5-minute prepaid call cycle - Prometheus metrics + Grafana dashboard with CCR latency histograms and Result-Code breakdowns
Why this pairs with cdr-pipeline
cdr-pipeline and diameter-cc are deliberately built as a pair — same business problem (mobile billing), opposite latency regimes.
| Aspect | cdr-pipeline | diameter-cc |
|---|---|---|
| Billing model | Post-paid | Prepaid |
| Latency budget | Seconds–minutes (batch) | <50 ms p99 (real-time) |
| Trigger | CDR file / Kafka after call | CCR while call is in progress |
| Risk if it fails | Reconciliation needed | User gets free service or call drops |
| Idempotency key | event_id |
(Session-Id, CC-Request-Number) |
| Storage | MySQL rated + MongoDB raw | Postgres credit_account + reservation |
| Wire protocol | Kafka | Diameter (TCP, RFC 6733) |
Both repos apply the same idempotency discipline — a replayed message is detected by its key and the original answer is returned byte-identically.
flowchart LR
A[diameter-test-client<br/>PDN Gateway sim] -- TCP 3868 --> B[jdiameter Stack]
B -- NetworkReqListener --> C[CreditControlListener]
C -- AvpCodec.parse --> D[CreditControlRequest]
D --> E[CreditControlService]
E --> F[LedgerService]
F -- @Transactional --> H[(Postgres:<br/>credit_account,<br/>cc_session,<br/>reservation,<br/>ledger_transaction)]
F --> I[CreditControlAnswer]
I -- AvpCodec.build --> J[Answer Message]
J --> B
B -- CCA-I/U/T --> A
git clone git@github.com:soneeee22000/diameter-cc.git
cd diameter-cc
docker compose up -d postgres prometheus grafana
./mvnw spring-boot:run -pl server
# in another terminal:
./mvnw exec:java -pl test-client \
-Dexec.mainClass=dev.pseonkyaw.diametercctest.DiameterTestClientWatch the credit balance debit live in Postgres and CCR latency in Grafana at localhost:3001 (anonymous viewer enabled).
This is a 30-hour weekend portfolio piece. The following are explicit, deliberate cuts:
- SCTP transport — TCP only (RFC 6733 permits both)
- IPv6 peer transport — IPv4 only
- Multi-realm DRA / agent routing — single peer pair
- Full RFC 4006 §5.7 failure-handling matrix — CCFH and DDFH not implemented
- Multi-Service-Credit-Control AVP combinatorics — one quota per session (CC-Time)
- Disk-based session recovery / clustering / HA — single-instance, in-memory peer state
- 5G SBI HTTP/2 Npcf wrapper — Diameter Gy only
- TLS / DTLS — plaintext for demo
These are documented gaps, not oversights. Each is a real production concern scoped out to keep the portfolio piece focused and shippable.
Day 1 progress (~30h weekend budget):
- Block 1 — Maven multi-module scaffold, Spring Boot wiring, jdiameter peer-handshake spike (CER/CEA verified end-to-end)
- Block 2 — Production
CreditControlListenerwired into Spring lifecycle - Block 3 —
AvpCodecparses CCR-Initial / Update / Termination - Block 4 — JPA entities + repositories (CreditAccount, CcSession, Reservation, LedgerTransaction) + Testcontainers wiring
- Block 5 —
LedgerServicetransactional reserve / debit / refund primitives - Block 6 —
CreditControlService.handleInitialreturns CCA-I with quota grant; idempotency cache short-circuits replays - Block 7 — End-to-end CCR-Initial integration test (Docker-gated)
- Day 2 Block 1+2 — CCR-Update + CCR-Terminate full flows (refund of unused, debit of used, optional new grant, session close)
- Day 2 Block 3 — End-to-end idempotency replay integration test
- Day 2 Block 5 — Micrometer metrics:
diameter_ccr_total{type, result_code},diameter_ccr_latency_seconds{type}(SLO histogram),diameter_active_sessions,diameter_quota_granted_units_total,diameter_quota_used_units_total,diameter_idempotent_replay_total - Day 2 Block 6 — Provisioned Grafana dashboard with 7 panels (server up, active sessions, replays, CCR rate by type, Result-Code breakdown, p50/p95/p99 latency, grant vs use)
- Day 3 — Live demo verified (server + Postgres + Prometheus + Grafana), README hero GIF embedded, all 25 tests run green against Docker
Test scoreboard: 25 tests, 0 failures, 11 skipped (Docker-gated).
MIT — see LICENSE.
