fix(postgres): accept full_page_writes off on Amazon Aurora PostgreSQL only
maven / build (push) Successful in 6m14s
build / maven (push) Successful in 6m14s

Generation qualification refused every Amazon Aurora PostgreSQL server:
Aurora always reports full_page_writes = off and has no parameter to change
it, because its storage layer does not write torn pages.

The settings query now also returns
to_regprocedure('aurora_version()') IS NOT NULL (true on Aurora, false on
stock PostgreSQL, never an error). full_page_writes = off is accepted only
when that is true. Stock PostgreSQL must still report it on, and fsync on,
synchronous_commit on or remote_apply and every other check are unchanged
on both. The refusal text now says full-page writes are not required on
Aurora.

Tests run on a real postgres:17 engine whose settings are really changed;
only Aurora is simulated, by creating a function named aurora_version() in
the test database.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
james.maesandClaude Fable 5.2 committed 2026-11-09 20:25:55 -06:00
1 parent 1dc265647d
commit cfbf0744f9
4 files changed
+168 -11

No files matched your search

@@ -41,6 +41,9 @@ class PostgresGenerationQueueTest extends GenerationQueueJdbcTest
{
private static PostgresGenerationDatabase postgres;
// The engine the fixture calls of the running test address: the shared pinned one unless that test started its own.
private PostgresGenerationDatabase current;
/***********************************************************************
** One exact disposable engine per suite; all test cases use new databases.
***********************************************************************/
@@ -68,7 +71,7 @@ class PostgresGenerationQueueTest extends GenerationQueueJdbcTest
@Override
protected RDBMSBackendMetaData database(String name, String defaultUrl) throws Exception
{
return (postgres.database(name));
return (engine().database(name));
}
/***********************************************************************
@@ -77,7 +80,7 @@ class PostgresGenerationQueueTest extends GenerationQueueJdbcTest
@Override
protected Connection connection() throws Exception
{
return (postgres.connection());
return (engine().connection());
}
/***********************************************************************
@@ -86,7 +89,15 @@ class PostgresGenerationQueueTest extends GenerationQueueJdbcTest
@Override
protected void sql(String source) throws Exception
{
postgres.sql(source);
engine().sql(source);
}
/***********************************************************************
** The shared pinned engine, or the unpinned one a single test started.
***********************************************************************/
private PostgresGenerationDatabase engine()
{
return (current == null ? postgres : current);
}
/***********************************************************************
@@ -178,6 +189,86 @@ class PostgresGenerationQueueTest extends GenerationQueueJdbcTest
assertThat(queue.status(enqueue("restored"), -1, 10).items()).hasSize(2);
}
/***************************************************************************
** Amazon Aurora PostgreSQL always runs with full_page_writes off (its storage
** layer does not write torn pages), so that one setting is accepted there and
** nowhere else. The engine is a real PostgreSQL 17 whose settings are really
** changed. Only Aurora itself is simulated: a function named aurora_version()
** is created in the test database, so the same to_regprocedure detection that
** is true on Aurora resolves here.
***************************************************************************/
@Test
void fullPageWritesOffIsAcceptedOnlyOnAuroraAndNothingElseIsRelaxed() throws Exception
{
current = new PostgresGenerationDatabase(false);
try
{
// Rebuild the instance, schema and definitions on the unpinned engine (durable defaults, so this qualifies).
cleanup();
setup();
assertThat(queue.advance(enqueue("stock-durable")).state()).isEqualTo("GENERATED");
// Stock PostgreSQL with full_page_writes off is refused, and the refusal states the rule.
current.system("full_page_writes", "off");
assertThatThrownBy(() -> enqueue("stock-torn-pages")).isInstanceOf(QException.class)
.hasMessageContaining("full-page writes (except on Amazon Aurora PostgreSQL)").hasMessageContaining("not satisfied: synchronous commit");
// The same server, now resolving aurora_version(), is accepted and work really commits.
sql("CREATE FUNCTION aurora_version() RETURNS text LANGUAGE sql IMMUTABLE AS $$ SELECT '17.9'::text $$");
ConnectionManager.closeConnectionProviders();
assertThat(queue.advance(enqueue("aurora")).state()).isEqualTo("GENERATED");
// Aurora relaxes nothing else: fsync off is refused.
current.system("fsync", "off");
assertThatThrownBy(() -> enqueue("aurora-no-fsync")).isInstanceOf(QException.class).hasMessageContaining("not satisfied: synchronous commit");
current.system("fsync", "on");
// Aurora relaxes nothing else: asynchronous commit is refused.
sql("ALTER ROLE generation_app SET synchronous_commit='off'");
ConnectionManager.closeConnectionProviders();
assertThatThrownBy(() -> enqueue("aurora-async-commit")).isInstanceOf(QException.class).hasMessageContaining("not satisfied: synchronous commit");
sql("ALTER ROLE generation_app SET synchronous_commit='on'");
ConnectionManager.closeConnectionProviders();
assertThat(enqueue("aurora-restored")).isNotNull();
// Without the function the server is stock PostgreSQL again and is refused again.
sql("DROP FUNCTION aurora_version()");
ConnectionManager.closeConnectionProviders();
assertThatThrownBy(() -> enqueue("stock-again")).isInstanceOf(QException.class).hasMessageContaining("not satisfied: synchronous commit");
}
finally
{
ConnectionManager.closeConnectionProviders();
current.close();
current = null;
}
}
/***************************************************************************
** The durability decision itself, for the combinations the live case does
** not reach: no server and nothing simulated, only the values of one row.
***************************************************************************/
@Test
void durabilityRuleAcceptsOnlyTheAuroraFullPageWritesException()
{
assertThat(GenerationPostgres.durable("on", "on", "on", false)).isTrue();
assertThat(GenerationPostgres.durable("remote_apply", "on", "on", false)).isTrue();
assertThat(GenerationPostgres.durable("on", "on", "on", true)).isTrue();
assertThat(GenerationPostgres.durable("on", "on", "off", true)).isTrue();
assertThat(GenerationPostgres.durable("remote_apply", "on", "off", true)).isTrue();
assertThat(GenerationPostgres.durable("on", "on", "off", false)).isFalse();
assertThat(GenerationPostgres.durable("on", "off", "on", false)).isFalse();
assertThat(GenerationPostgres.durable("on", "off", "off", true)).isFalse();
for(String weak : List.of("off", "local", "remote_write"))
{
assertThat(GenerationPostgres.durable(weak, "on", "on", false)).isFalse();
assertThat(GenerationPostgres.durable(weak, "on", "off", true)).isFalse();
}
assertThat(GenerationPostgres.durable(null, "on", "on", true)).isFalse();
assertThat(GenerationPostgres.durable("on", null, "on", true)).isFalse();
assertThat(GenerationPostgres.durable("on", "on", null, true)).isFalse();
}
/***************************************************************************
** The native identity must be the postgres module on a real PostgreSQL17 server, not a vendor label.
***************************************************************************/