Skip to content

feat!: rebuild Ogiri 0.1.0 for Spring Boot, PostgreSQL, MySQL and optional caching - #60

Open
mosobande wants to merge 13 commits into
orifrom
build/ogiri-0.1.0
Open

feat!: rebuild Ogiri 0.1.0 for Spring Boot, PostgreSQL, MySQL and optional caching#60
mosobande wants to merge 13 commits into
orifrom
build/ogiri-0.1.0

Conversation

@mosobande

@mosobande mosobande commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Purpose and current scope

Rebuild the unpublished library at 0.1.0 for low-setup Spring Boot 4.1 / Java 17+ integration. This is a new API and schema lineage, not a compatible v3/v4 downgrade. It supersedes the earlier PostgreSQL-only candidate in this PR and remains an alternative to #59, not a stack on it. No merge, release tag or Maven Central publication has occurred.

Verified head: 5119a06f42fab49ed1b29fd38ddcebe33edad608, based on ori at d3240a405722689e608873a65c419ef0c3658739.

What ships

Two code artifacts and one parent POM:

  • com.quantipixels.ogiri:ogiri:0.1.0: Java session lifecycle using Spring JDBC and Spring transaction management, supporting PostgreSQL and MySQL. No JPA entity hierarchy, custom storage framework, required cache provider or Kotlin runtime.
  • com.quantipixels.ogiri:ogiri-spring-boot-starter:0.1.0: conditional Boot configuration, validated properties/IDE metadata, Spring's native opaque-bearer pipeline, reusable security-chain integration and optional JSON session endpoints.

The default consumer supplies its existing UserDetailsService, ordinary spring.datasource.* settings, one database driver and application-owned schema migrations. Spring's AuthenticationConfiguration reuses application authentication providers/manager and password encoding. OgiriAccounts handles stable IDs, mutable login names and tenant-aware identity when the default immutable-username mapping is unsuitable.

Built-in endpoints cover sign-in, current session, session listing, owned-session revocation, sign-out and revoke-all. The default security chain exists only when the application has no chain. Existing/multiple chains use OgiriSecurity without losing application authorization or other authentication methods. Endpoints and the whole starter can be disabled independently. Duplicate Authorization-header rejection and JSON-login CSRF handling are packaged rather than copied from the example. Account registration, recovery, MFA and OAuth/OIDC federation are not reimplemented.

Database and security behavior

Both engines use the same lifecycle and contract tests; database-specific time expressions, lock-row insertion and DDL stay internal. Packaged PostgreSQL/MySQL templates use ogiri_sessions plus stable digest-keyed ogiri_subject_locks. MySQL requires InnoDB. Identity comparisons preserve the complete (realm, tenantId, stableSubjectId) without case-folding or padding ambiguity. Ogiri never runs DDL at startup or reserves an application Flyway version.

Credentials contain 256 random bits; only SHA-256 digests are stored. Fixed expiry and session limits remain configurable; admission rejects rather than silently evicting another device. Default authentication performs one indexed session read and no writes, then resolves current account status and authorities. Spring manages independent mutation commits and JDBC/JPA transaction suspension for authoritative database reads. An outer transaction holding a connection needs spare pool capacity; ordinary use is exercised with one pooled connection.

Optional cache support, disabled by default

Developers can opt into session caching through their existing Spring CacheManager and a dedicated region:

ogiri:
  cache:
    enabled: true
    name: my-app.ogiri.sessions
    max-age: 5s

The default age when enabled is five seconds; accepted values are 1ms through 1m. Every hit checks its original validation age and absolute session expiry. Hits and late concurrent fills do not restart that age. Keys contain token digests; values contain serializable session metadata. Ogiri does not cache plaintext tokens, passwords, account status, authorities, principals, misses or failed database lookups. Account checks still run on each request, with freshness determined by the application's account adapter.

The application chooses the provider and eviction/size/serialization policy. Caffeine with Boot auto-configuration and Spring's store-by-value concurrent-map provider are exercised; a particular Redis deployment/custom serializer is not thereby certified. Enabling the feature without a cache manager or named region fails startup. A custom JdbcSessions bean takes precedence. No provider is added to the default runtime, and Ogiri does not globally enable application caching or disable credential erasure.

Opt-in changes consistency: successful revoke/revoke-all evicts after the independent SQL commit, but eviction is best effort. Other JVMs' local caches, concurrent refills, direct SQL changes and provider failures can retain validation until its original age/expiry deadline. Bounds assume synchronized clocks and trusted cache storage. A valid cache hit can survive a database outage for the remaining window if account checks succeed. Cache read failures fall back to SQL; cache eviction failure cannot undo a committed revocation. Leave caching disabled for strict per-request session freshness. README and SECURITY.md include the integration recipe and these limitations.

Verified results at the current head

The final PR build passed all four combinations: Java 17 and 25 × PostgreSQL 16 and MySQL 8.4. CodeQL analysis completed successfully for the same head. These are current-head results, not checks borrowed from the earlier PostgreSQL-only candidate.

All four downloaded build artifacts matched GitHub's SHA-256 digests. Their verification records each contain 36 executed test invocations, zero failures, errors or skips:

Boundary Invocations per configuration
JDBC lifecycle, isolation, transaction/concurrency/fault and cache integration 13
Cache age, expiry, refill race, provider faults and serialization 6
Boot configuration, backoff and invalid cache setup 5
Account/principal integration, including cached and uncached status checks 4
Independent installed-artifact HTTP consumers, default and Boot/Caffeine 6
Existing multi-chain identity mapping and JPA composition 2

Counts include parameter variants and reuse of the same HTTP contract with caching on/off. The matrix therefore executes 144 invocations, not 144 unique scenarios. Tests observe real database/HTTP behavior and deterministic cache boundaries rather than mock call choreography.

The same final build also verified:

  • Both schema templates extracted from the built JAR, nonempty sources/Javadoc, Boot auto-configuration imports and all nine configuration properties, including the three cache properties.
  • Resolved library and example runtime dependency inventories against OSV. The inspected result is results: []; this is no known findings in those scanned graphs at the run, not universal vulnerability clearance or a scan of every optional provider/test dependency.
  • An actual unsigned local Maven deployment containing three POMs and six binary/source/Javadoc JARs, with SHA-1/MD5 sidecars. No Central upload or publisher signing was performed.

Before delivery, source verification tested both engines on Java 17 and non-force-pushed coherent native source commits. Its source head was 69f81e9afb652492c6f0ef6edece350b855dcb89; the final head adds the packaged cache-metadata gate. An initial serialization test exposed a missing standalone Spring class-loader callback; the fixture was corrected without weakening the value-copy assertion. The temporary cache-verification workflow has been removed from the execution branch.

Publication and limits

Maven publication preparation includes the parent/core/starter POMs, six binary/source/Javadoc JARs, an opt-in signing profile, and the verified local deployment/bundle rehearsal. Actual signing credentials, namespace ownership, Central acceptance and public publication remain publisher-controlled gates. The tested consumer pins Tomcat 11.0.25; host applications retain dependency-management ownership.

The opt-in storage benchmark harness remains available. Earlier local no-cache measurements exclude HTTP and account lookup; no cache speedup or production throughput guarantee is claimed. No automatic refresh/rotation, cookie transport, reactive adapter, independent human security clearance or universal JDBC portability is claimed.

Earlier session HTML and PostgreSQL-only descriptions are historical snapshots, not the current handoff. The earlier no-cache assessment still explains the uncached default; this revision adds the subsequently requested opt-in capability.

Copy link
Copy Markdown
Collaborator Author

Cache assessment: do not add cross-request authentication caching to 0.1.0

Reviewed candidate: d3bcd23411eda70f799e958a7db4299f22ffd816 against ori at d3240a405722689e608873a65c419ef0c3658739. This assessment concerns the Spring Boot starter with PostgreSQL and MySQL, not the superseded PostgreSQL-only design in the older PR description.

Decision: caching has not earned a production dependency, configuration option or durable test suite. No cache implementation was added. This is a decision under current evidence, not a claim that no adopter will ever benefit from caching.

Evidence inspected

JdbcSessions.authenticate validates canonical token format before I/O, then performs one indexed session read with no writes. The Spring introspector subsequently loads current account status and authorities. OgiriEndpoints.currentSession reuses the verified Session in the current principal, so current-session endpoints do not re-query the same credential. Mutation results are committed before subsequent authoritative reads, and transaction suspension avoids stale JDBC/JPA snapshots.

Recovered the two original benchmark JSON files from artifact 10012065646 in run 34104926861, checking the archive SHA-256 7f96a3b15f9a0cf156bbd4a6ab139b44a1244b104328293c52198de9be6c0915. Readback run 34113824349 prints their actual values. This was artifact inspection, not a newly executed benchmark. Measured source was 09126f274125c8c13071a09885118f1407adeb1d; the comparison to the reviewed head changes CI/publishing guidance/README, not runtime or benchmark source.

Database Workers Samples Lookups/second p50, microseconds p95, microseconds p99, microseconds
PostgreSQL 1 2,000 3,856.5 246.0 312.3 384.8
PostgreSQL 8 16,000 12,318.9 551.7 1,260.3 2,162.9
MySQL 1 2,000 2,492.7 385.9 492.0 725.0
MySQL 8 16,000 7,253.5 964.1 2,069.3 3,094.4

Boundary: Java 17, local PostgreSQL/MySQL containers, 10,000 inserted sessions, 1,000 warmup calls, eight pooled connections, repeated lookup of ONE token. These are short warm-path storage measurements, excluding account directory, HTTP, realistic session cardinality/access distribution, remote-database latency and concurrent revocation workload. They do not establish sustained production capacity or a cache/no-cache speedup. There is no supplied production latency budget or demonstrated production session-query bottleneck that makes caching necessary.

Why a generic Spring cache is not the answer

A positive session cache can accept a revoked token until invalidation/expiry. Caching the complete principal also delays account disablement and permission changes. An illustrative cache-aside race: reader obtains a live SQL row; revoker commits and evicts; reader installs its old result after eviction. A TTL bounds some exposure but does not preserve immediate revocation, and expiry must not be extended by cache hits. An authoritative version check on every hit preserves freshness by retaining the shared-store read we were trying to remove.

Spring's Cache/CacheManager abstraction is appropriate when caching is justified, but does not itself solve multi-process propagation or concurrent load/eviction: Spring cache strategies. A local cache plus Redis Pub/Sub is not reliable revocation delivery: Redis documents at-most-once delivery. The same performance-versus-revocation trade-off is described in RFC 7662 section 4; this is an applicable design consideration, not a claim that Ogiri implements the remote RFC endpoint.

Scope decisions

  • Keep request-local verified-session reuse: already implemented; no new cache is needed.
  • Do not cache successful session validation, security principals, live account status or authorities by default.
  • Do not add a negative-token cache without evidence of repeated-invalid-token traffic. Unique random tokens defeat its hit rate; malformed tokens already fail before SQL. Rate limiting is a different control.
  • Application-owned, non-security profile/reference data can use Spring Cache independently. The existing OgiriAccounts/UserDetailsService integration does not need a new Ogiri cache SPI. Do not assume caching UserDetails preserves fresh account status or secret-erasure semantics.

Reopen only when representative measurements show session/account lookup violating the consumer's latency or database-capacity budget and a cached variant materially improves it. Before shipping that variant, establish the allowed revocation/role-change staleness and prove expiry, concurrent fill-after-revoke, revoke-all, cross-node invalidation and outage behaviour. Reuse Spring Cache/CacheManager rather than inventing a cache framework; provider requirements and security semantics must be explicit.

QP guidance applied: ro-wo (necessity is unproved), architect/module-design (ownership and consistency), pare (no speculative mechanism or tests), technical-writing/yo-slop (decision and evidence boundaries). Library source, dependencies, public API and version remain unchanged. The temporary artifact-readback workflow was removed after inspection.

@mosobande mosobande changed the title feat!: rebuild Ogiri 0.1.0 as a focused Java session library feat!: rebuild Ogiri 0.1.0 for Spring Boot, PostgreSQL, MySQL and optional caching Sep 7, 2026
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