Skip to content

Release 0.3.0 - #36

Merged
thomasdarimont merged 2 commits into
mainfrom
release/0.3.0
Oct 6, 2026
Merged

thomasdarimont merged 2 commits into
mainfrom
release/0.3.0

Conversation

@thomasdarimont

Copy link
Copy Markdown
Collaborator

Release 0.3.0, then 0.4.0-SNAPSHOT.

Merge with a merge commit or a rebase, not a squash: the commit "Release 0.3.0" has to reach main unchanged, etc/release.sh release tags it there.

Release notes, from CHANGELOG.md:

POLL delivery that survives restarts and runs on several instances: acknowledgements wait in a
store, optionally in the database, long polling keeps a request outstanding, and a SET is claimed
while its handlers run so that two instances never acknowledge a SET one of them failed on.
The JDBC schema gained a column and a table; migration scripts and startup upgrades come with it.
Handlers get typed access to CAEP and RISC events with SsfCaepEventHandler and SsfRiscEventHandler,
and the documentation now says in plain words that processing is at least once, with idempotencyKey()
as the key for a handler's side effects.

Added

  • POLL delivery keeps the acknowledgements and error reports it owes the transmitter in an
    SsfPollAckStore until a poll request has carried them: in memory by default, or durably with
    JdbcSsfPollAckStore (table EASYSSF_POLL_ACK, auto-configured with the other JDBC stores,
    easyssf.receiver.jdbc.ack-retention), so that a SET handled right before a restart is
    acknowledged afterwards instead of being delivered again. SsfPoller.stop() sends the pending
    acknowledgements with a last request; the health details show them as pendingAcks and the
    gauge easyssf.receiver.poll.pending-acks counts them.

  • Long polling (RFC 8936, section 2.5): SsfPoller.setLongPolling(hold) keeps one request
    outstanding that the transmitter holds until SETs are available; easyssf.receiver.poll.long-polling
    and poll.long-polling-hold in the Spring Boot starter, off by default. SsfHttpRequest carries an
    optional timeout per request. TestTransmitter holds long polls for setLongPollHold.
    SsfPoller.setThreadFactory lets a framework create the polling thread, or use a virtual one.

  • A SET is claimed in the SsfJtiDedupStore while its handlers run (Claim.IN_PROGRESS): another
    instance that receives it meanwhile neither handles nor acknowledges it (SsfSetInProgressException,
    metrics outcome in_progress), so a handler failure on the first instance is not masked. A claim
    older than the lease (easyssf.receiver.dedup.lease, 60 seconds) counts as abandoned and the SET is
    handled again. The claim carries a token that fences it: an instance whose handlers outlived the
    lease can neither complete nor forget the claim of the instance that took the SET over.

  • SsfEventToken.idempotencyKey() and SsfEventContext.idempotencyKey(), the issuer and jti of a
    SET, as the key for the side effects of a handler: processing is at least once, which the
    documentation now says in those words.

  • Typed CAEP and RISC events: SsfCaepEventHandler and SsfRiscEventHandler dispatch the events of a
    SET to a method per event (onSessionRevoked, onAssuranceLevelChange, onAccountDisabled, ...),
    with SsfCaepEvent and SsfRiscEvent from easyssf-core for the claims the specifications define
    (eventTimestamp(), initiatingEntity(), the localized reasonAdmin() and reasonUser(),
    credentialType(), currentLevel(), changeDirection(), reason(), newValue(), ...). An event
    type in the CAEP or RISC namespace that easyssf does not know yet is SsfCaepEventKind.OTHER or
    SsfRiscEventKind.OTHER and reaches onOtherCaepEvent or onOtherRiscEvent. SsfEventContext.eventFor
    stays for raw access. SsfEventTypes gained RiscSessionsRevoked (deprecated by RISC, still sent) and
    isCaepEvent/isRiscEvent; SsfEventTimestamps reads an event_timestamp in seconds or milliseconds.

  • Schema migration: easyssf-receiver-jdbc ships versioned migration scripts in
    org/easyssf/receiver/jdbc/migration/ (Flyway naming, plain SQL), and JdbcSsfSchema.prepareTable
    adds the columns a release introduced to an existing table where it may create tables
    (initialize-schema embedded or always); otherwise it names the statements to run.

Changed

  • SsfJtiDedupStore has claim, which returns a Claim with the State of the SET and the token of
    the claim granted, and processed and forget, which take that claim; seenBefore is a deprecated
    default method. The table EASYSSF_PROCESSED_SET gained the column STATE; existing tables need
    ALTER TABLE EASYSSF_PROCESSED_SET ADD STATE VARCHAR(16) DEFAULT 'PROCESSED' NOT NULL (the migration
    script V0_3_0__dedup_state_and_poll_acks.sql), applied on startup where tables may be created and
    named in the error otherwise.
  • The schema of easyssf-receiver-jdbc has a third table, EASYSSF_POLL_ACK; create it with the
    statements of schema.sql where the tables are not created on startup.
  • SsfPoller polls on a thread of its own instead of a scheduled executor, and the in-memory stores
    lock with ReentrantLock instead of synchronized, which pins virtual threads before JDK 24.
  • SsfJdbcOperations.query(sql, maxRows, mapper, args) limits a result through the JDBC driver, so
    the stores need no dialect-specific SQL; the JDBC stores are now tested on MySQL as well as on H2
    and PostgreSQL.

Signed-off-by: Thomas Darimont <thomas.darimont@googlemail.com>
Signed-off-by: Thomas Darimont <thomas.darimont@googlemail.com>
@thomasdarimont
thomasdarimont merged commit a548249 into main Oct 6, 2026
10 checks passed
@thomasdarimont
thomasdarimont deleted the release/0.3.0 branch October 6, 2026 00:06
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.

1 participant