mirror of
https://github.com/neondatabase/neon.git
synced 2026-01-04 12:02:55 +00:00
storcon: validate new_sk_set before starting safekeeper migration (#12546)
## Problem We don't validate the validity of the `new_sk_set` before starting the migration. It is validated later, so the migration to an invalid safekeeper set will fail anyway. But at this point we might already commited an invalid `new_sk_set` to the database and there is no `abort` command yet (I ran into this issue in neon_local and ruined the timeline :) - Part of https://github.com/neondatabase/neon/issues/11669 ## Summary of changes - Add safekeeper count and safekeeper duplication checks before starting the migration - Test that we validate the `new_sk_set` before starting the migration - Add `force` option to the `TimelineSafekeeperMigrateRequest` to disable not-mandatory checks
This commit is contained in:
@@ -2,6 +2,9 @@ from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import pytest
|
||||
from fixtures.neon_fixtures import StorageControllerApiException
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fixtures.neon_fixtures import NeonEnvBuilder
|
||||
|
||||
@@ -75,3 +78,38 @@ def test_safekeeper_migration_simple(neon_env_builder: NeonEnvBuilder):
|
||||
ep.start(safekeeper_generation=1, safekeepers=[3])
|
||||
|
||||
assert ep.safe_psql("SELECT * FROM t") == [(i,) for i in range(1, 4)]
|
||||
|
||||
|
||||
def test_new_sk_set_validation(neon_env_builder: NeonEnvBuilder):
|
||||
"""
|
||||
Test that safekeeper_migrate validates the new_sk_set before starting the migration.
|
||||
"""
|
||||
neon_env_builder.num_safekeepers = 3
|
||||
neon_env_builder.storage_controller_config = {
|
||||
"timelines_onto_safekeepers": True,
|
||||
"timeline_safekeeper_count": 2,
|
||||
}
|
||||
env = neon_env_builder.init_start()
|
||||
|
||||
def expect_fail(sk_set: list[int], match: str):
|
||||
with pytest.raises(StorageControllerApiException, match=match):
|
||||
env.storage_controller.migrate_safekeepers(
|
||||
env.initial_tenant, env.initial_timeline, sk_set
|
||||
)
|
||||
# Check that we failed before commiting to the database.
|
||||
mconf = env.storage_controller.timeline_locate(env.initial_tenant, env.initial_timeline)
|
||||
assert mconf["generation"] == 1
|
||||
|
||||
expect_fail([], "safekeeper set is empty")
|
||||
expect_fail([1], "must have at least 2 safekeepers")
|
||||
expect_fail([1, 1], "duplicate safekeeper")
|
||||
expect_fail([1, 100500], "does not exist")
|
||||
|
||||
mconf = env.storage_controller.timeline_locate(env.initial_tenant, env.initial_timeline)
|
||||
sk_set = mconf["sk_set"]
|
||||
assert len(sk_set) == 2
|
||||
|
||||
decom_sk = [sk.id for sk in env.safekeepers if sk.id not in sk_set][0]
|
||||
env.storage_controller.safekeeper_scheduling_policy(decom_sk, "Decomissioned")
|
||||
|
||||
expect_fail([sk_set[0], decom_sk], "decomissioned")
|
||||
|
||||
Reference in New Issue
Block a user