smolnar82 commented on code in PR #1456:
URL: https://github.com/apache/knox/pull/1456#discussion_r4347248988
##########
gateway-server/src/test/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/JdbcTrustedOidcIssuerServiceTest.java:
##########
@@ -493,10 +497,274 @@ public void testConcurrentInitDoesNotReinitialize()
throws Exception {
sentinel, FieldUtils.readField(service, "database", true));
}
+ // ------------------------------------------------------------------
+ // Registry snapshot TTL (HA eventual consistency)
+ // ------------------------------------------------------------------
+ // Direct JDBC writes stand in for another Knox node writing to the shared
table. With a TTL of
+ // 10s the effective (jittered) TTL is in [9s, 10s] and the background
refresh is due after at
+ // most 5s, so +6s means "refresh due, not expired" and +11s means "expired".
+
+ private static final int TTL_SECS = 10;
+
+ @Test
+ public void testRemoteRegistrationVisibleAfterBackgroundRefresh() throws
Exception {
+ final AtomicLong now = new AtomicLong();
+ final JdbcTrustedOidcIssuerService ttlService = ttlService(TTL_SECS, now);
+ insertRow("https://remote.example.com", false, Instant.now());
+
+ assertFalse("A fresh snapshot is served without hitting the DB",
+ ttlService.isTrusted("https://remote.example.com"));
+
+ advance(now, 6);
+ ttlService.isTrusted("https://remote.example.com"); // triggers the
background refresh, served stale
+ assertTrue("Remote registration must be visible once the background
refresh completed",
+ ttlService.isTrusted("https://remote.example.com"));
+ }
+
+ @Test
+ public void testRemoteDeregistrationNeverServedAfterTtl() throws Exception {
+ final AtomicLong now = new AtomicLong();
+ final JdbcTrustedOidcIssuerService ttlService = ttlService(TTL_SECS, now);
+ ttlService.register(issuer("https://issuer.example.com", false));
+ deleteRow("https://issuer.example.com");
+
+ // No read in between, so no background refresh ran: the expired snapshot
must not be served
+ advance(now, 11);
+ assertFalse("A revoked issuer must not be trusted once the TTL elapsed",
+ ttlService.isTrusted("https://issuer.example.com"));
+ }
+
+ @Test
+ public void testNonPositiveTtlFallsBackToDefault() throws Exception {
+ final AtomicLong now = new AtomicLong();
+ final JdbcTrustedOidcIssuerService ttlService = ttlService(0, now);
+ insertRow("https://remote.example.com", false, Instant.now());
+
+ // Default TTL is 60s: effective TTL in [54s, 60s], refresh due after at
least 27s
+ advance(now, 25);
+ ttlService.isTrusted("https://remote.example.com");
+ assertFalse(ttlService.isTrusted("https://remote.example.com"));
+
+ advance(now, 36);
+ assertTrue(ttlService.isTrusted("https://remote.example.com"));
+ }
+
+ /**
+ * DB outage: the snapshot is served stale until the TTL, a failed refresh
neither extends the
+ * TTL nor is retried on every request, then the registry fails closed and
recovers once the DB
+ * is readable again.
+ */
+ @Test
+ public void testDatabaseOutageServesStaleThenFailsClosedThenRecovers()
throws Exception {
+ final AtomicLong now = new AtomicLong();
+ final JdbcTrustedOidcIssuerService ttlService = ttlService(TTL_SECS, now);
+ ttlService.register(issuer("https://issuer.example.com", false));
+ final TrustedOidcIssuerDatabase realDatabase =
+ (TrustedOidcIssuerDatabase) FieldUtils.readField(ttlService,
"database", true);
+
+ final TrustedOidcIssuerDatabase failingDb =
EasyMock.createMock(TrustedOidcIssuerDatabase.class);
+ EasyMock.expect(failingDb.selectAll()).andThrow(new SQLException("database
is down")).times(2);
+ EasyMock.replay(failingDb);
+ useDatabase(ttlService, failingDb);
+
+ advance(now, 6);
+ for (int i = 0; i < 5; i++) {
+ assertTrue("The cached snapshot must be served while it is younger than
the TTL",
+ ttlService.isTrusted("https://issuer.example.com"));
+ }
+
+ advance(now, 5); // 11s after the last successful load, although the
failed refresh ran at 6s
+ assertFalse("The registry must fail closed once the snapshot expired",
+ ttlService.isTrusted("https://issuer.example.com"));
+ assertTrue(ttlService.list().isEmpty());
+ assertTrue(registrySnapshotLoader(ttlService).isFailedClosed());
+ // One failed background refresh at 6s plus one failed load at 11s: no
per-request DB retries
+ EasyMock.verify(failingDb);
+
+ useDatabase(ttlService, realDatabase);
+ advance(now, 6);
+ ttlService.isTrusted("https://issuer.example.com"); // triggers the
background refresh
Review Comment:
Agreed: the triggering read is served the fail-closed snapshot, so it
returns false. I'll do it in the next patchset.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]