fix(postgres): accept full_page_writes off on Amazon Aurora PostgreSQL only
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:
1 parent
1dc265647d
commit
cfbf0744f9
4 files changed
+168
-11
No files matched your search
+94
-3
@@ -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.
|
||||
***************************************************************************/
|
||||
|
||||
Reference in new issue
Block a user