mirror of
https://github.com/neondatabase/neon.git
synced 2026-05-25 09:00:37 +00:00
neon_local: fix endpoint api to prevent two primary endpoints (#5520)
`neon_local endpoint` subcommand currently allows creating two primary endpoints for the same branch which leads to shutdown of both endpoints `neon_local endpoint start` new behavior: 1. Fail if endpoint doesn't exist 2. Fail if two primary conflict detected Fixes #4959 Closes #5426 Signed-off-by: Rahul Modpur <rmodpur2@gmail.com> Co-authored-by: Joonas Koivunen <joonas@neon.tech>
This commit is contained in:
@@ -1414,34 +1414,19 @@ class NeonCli(AbstractNeonCli):
|
||||
def endpoint_start(
|
||||
self,
|
||||
endpoint_id: str,
|
||||
pg_port: int,
|
||||
http_port: int,
|
||||
safekeepers: Optional[List[int]] = None,
|
||||
tenant_id: Optional[TenantId] = None,
|
||||
lsn: Optional[Lsn] = None,
|
||||
branch_name: Optional[str] = None,
|
||||
remote_ext_config: Optional[str] = None,
|
||||
pageserver_id: Optional[int] = None,
|
||||
) -> "subprocess.CompletedProcess[str]":
|
||||
args = [
|
||||
"endpoint",
|
||||
"start",
|
||||
"--tenant-id",
|
||||
str(tenant_id or self.env.initial_tenant),
|
||||
"--pg-version",
|
||||
self.env.pg_version,
|
||||
]
|
||||
if remote_ext_config is not None:
|
||||
args.extend(["--remote-ext-config", remote_ext_config])
|
||||
if lsn is not None:
|
||||
args.append(f"--lsn={lsn}")
|
||||
args.extend(["--pg-port", str(pg_port)])
|
||||
args.extend(["--http-port", str(http_port)])
|
||||
|
||||
if safekeepers is not None:
|
||||
args.extend(["--safekeepers", (",".join(map(str, safekeepers)))])
|
||||
if branch_name is not None:
|
||||
args.extend(["--branch-name", branch_name])
|
||||
if endpoint_id is not None:
|
||||
args.append(endpoint_id)
|
||||
if pageserver_id is not None:
|
||||
@@ -1468,15 +1453,12 @@ class NeonCli(AbstractNeonCli):
|
||||
def endpoint_stop(
|
||||
self,
|
||||
endpoint_id: str,
|
||||
tenant_id: Optional[TenantId] = None,
|
||||
destroy=False,
|
||||
check_return_code=True,
|
||||
) -> "subprocess.CompletedProcess[str]":
|
||||
args = [
|
||||
"endpoint",
|
||||
"stop",
|
||||
"--tenant-id",
|
||||
str(tenant_id or self.env.initial_tenant),
|
||||
]
|
||||
if destroy:
|
||||
args.append("--destroy")
|
||||
@@ -2507,9 +2489,6 @@ class Endpoint(PgProtocol):
|
||||
|
||||
self.env.neon_cli.endpoint_start(
|
||||
self.endpoint_id,
|
||||
pg_port=self.pg_port,
|
||||
http_port=self.http_port,
|
||||
tenant_id=self.tenant_id,
|
||||
safekeepers=self.active_safekeepers,
|
||||
remote_ext_config=remote_ext_config,
|
||||
pageserver_id=pageserver_id,
|
||||
@@ -2589,7 +2568,7 @@ class Endpoint(PgProtocol):
|
||||
if self.running:
|
||||
assert self.endpoint_id is not None
|
||||
self.env.neon_cli.endpoint_stop(
|
||||
self.endpoint_id, self.tenant_id, check_return_code=self.check_stop_result
|
||||
self.endpoint_id, check_return_code=self.check_stop_result
|
||||
)
|
||||
self.running = False
|
||||
|
||||
@@ -2603,7 +2582,7 @@ class Endpoint(PgProtocol):
|
||||
|
||||
assert self.endpoint_id is not None
|
||||
self.env.neon_cli.endpoint_stop(
|
||||
self.endpoint_id, self.tenant_id, True, check_return_code=self.check_stop_result
|
||||
self.endpoint_id, True, check_return_code=self.check_stop_result
|
||||
)
|
||||
self.endpoint_id = None
|
||||
self.running = False
|
||||
|
||||
@@ -434,8 +434,11 @@ def check_neon_works(
|
||||
|
||||
pg_port = port_distributor.get_port()
|
||||
http_port = port_distributor.get_port()
|
||||
cli_current.endpoint_start("main", pg_port=pg_port, http_port=http_port)
|
||||
request.addfinalizer(lambda: cli_current.endpoint_stop("main"))
|
||||
cli_current.endpoint_create(
|
||||
branch_name="main", pg_port=pg_port, http_port=http_port, endpoint_id="ep-main"
|
||||
)
|
||||
cli_current.endpoint_start("ep-main")
|
||||
request.addfinalizer(lambda: cli_current.endpoint_stop("ep-main"))
|
||||
|
||||
connstr = f"host=127.0.0.1 port={pg_port} user=cloud_admin dbname=postgres"
|
||||
pg_bin.run_capture(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import pytest
|
||||
from fixtures.neon_fixtures import NeonEnvBuilder
|
||||
from fixtures.port_distributor import PortDistributor
|
||||
|
||||
@@ -11,19 +12,50 @@ def test_neon_cli_basics(neon_env_builder: NeonEnvBuilder, port_distributor: Por
|
||||
env.neon_cli.start()
|
||||
env.neon_cli.create_tenant(tenant_id=env.initial_tenant, set_default=True)
|
||||
|
||||
main_branch_name = "main"
|
||||
pg_port = port_distributor.get_port()
|
||||
http_port = port_distributor.get_port()
|
||||
env.neon_cli.endpoint_start(
|
||||
endpoint_id="ep-basic-main", pg_port=pg_port, http_port=http_port
|
||||
env.neon_cli.endpoint_create(
|
||||
main_branch_name, pg_port, http_port, endpoint_id="ep-basic-main"
|
||||
)
|
||||
env.neon_cli.endpoint_start("ep-basic-main")
|
||||
|
||||
branch_name = "migration-check"
|
||||
|
||||
env.neon_cli.create_branch(new_branch_name=branch_name)
|
||||
env.neon_cli.create_branch(branch_name)
|
||||
pg_port = port_distributor.get_port()
|
||||
http_port = port_distributor.get_port()
|
||||
env.neon_cli.endpoint_start(
|
||||
f"ep-{branch_name}", pg_port, http_port, branch_name=branch_name
|
||||
env.neon_cli.endpoint_create(
|
||||
branch_name, pg_port, http_port, endpoint_id=f"ep-{branch_name}"
|
||||
)
|
||||
env.neon_cli.endpoint_start(f"ep-{branch_name}")
|
||||
finally:
|
||||
env.neon_cli.stop()
|
||||
|
||||
|
||||
def test_neon_two_primary_endpoints_fail(
|
||||
neon_env_builder: NeonEnvBuilder, port_distributor: PortDistributor
|
||||
):
|
||||
"""
|
||||
Two primary endpoints with same tenant and timeline will not run together
|
||||
"""
|
||||
env = neon_env_builder.init_start()
|
||||
branch_name = "main"
|
||||
|
||||
pg_port = port_distributor.get_port()
|
||||
http_port = port_distributor.get_port()
|
||||
env.neon_cli.endpoint_create(branch_name, pg_port, http_port, "ep1")
|
||||
|
||||
pg_port = port_distributor.get_port()
|
||||
http_port = port_distributor.get_port()
|
||||
# ep1 is not running so create will succeed
|
||||
env.neon_cli.endpoint_create(branch_name, pg_port, http_port, "ep2")
|
||||
|
||||
env.neon_cli.endpoint_start("ep1")
|
||||
|
||||
expected_message = f'attempting to create a duplicate primary endpoint on tenant {env.initial_tenant}, timeline {env.initial_timeline}: endpoint "ep1" exists already. please don\'t do this, it is not supported.'
|
||||
with pytest.raises(RuntimeError):
|
||||
assert expected_message in env.neon_cli.endpoint_start("ep2").stderr
|
||||
|
||||
env.neon_cli.endpoint_stop("ep1")
|
||||
# ep1 is stopped so create ep2 will succeed
|
||||
env.neon_cli.endpoint_start("ep2")
|
||||
|
||||
Reference in New Issue
Block a user