Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ psycopg2-binary = "^2.9.10"
boto3 = "*"
tenacity = "^9.1.2"
allure-pytest = "^2.15.0"
jubilant = "^1.4.0"
jubilant = "^1.5.0"
tomli-w = "^1.2.0"
tomli = "^2.3.0"

Expand All @@ -95,6 +95,7 @@ minversion = "6.0"
log_cli_level = "INFO"
asyncio_mode = "auto"
markers = ["juju3", "juju_secrets"]
addopts = "--exitfirst"

# Linting tools configuration
[tool.ruff]
Expand Down
35 changes: 21 additions & 14 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2314,6 +2314,26 @@ def _can_connect_to_postgresql(self) -> bool:
return False
return True

def _api_update_config(self, available_cpu_cores: int) -> None:
# Use config value if set, calculate otherwise
if self.config.experimental_max_connections:
max_connections = self.config.experimental_max_connections
else:
max_connections = max(4 * available_cpu_cores, 100)

cfg_patch = {
"max_connections": max_connections,
"max_prepared_transactions": self.config.memory_max_prepared_transactions,
"max_replication_slots": 25,
"max_wal_senders": 25,
"shared_buffers": self.config.memory_shared_buffers,
"wal_keep_size": self.config.durability_wal_keep_size,
}
base_patch = {}
if primary_endpoint := self.async_replication.get_primary_cluster_endpoint():
base_patch["standby_cluster"] = {"host": primary_endpoint}
self._patroni.bulk_update_parameters_controller_by_patroni(cfg_patch, base_patch)

def update_config(self, is_creating_backup: bool = False) -> bool:
"""Updates Patroni config file based on the existence of the TLS files."""
# Retrieve PostgreSQL parameters.
Expand Down Expand Up @@ -2379,20 +2399,7 @@ def update_config(self, is_creating_backup: bool = False) -> bool:
logger.debug("Early exit update_config: Patroni not started yet")
return False

# Use config value if set, calculate otherwise
if self.config.experimental_max_connections:
max_connections = self.config.experimental_max_connections
else:
max_connections = max(4 * available_cpu_cores, 100)

self._patroni.bulk_update_parameters_controller_by_patroni({
"max_connections": max_connections,
"max_prepared_transactions": self.config.memory_max_prepared_transactions,
"max_replication_slots": 25,
"max_wal_senders": 25,
"shared_buffers": self.config.memory_shared_buffers,
"wal_keep_size": self.config.durability_wal_keep_size,
})
self._api_update_config(available_cpu_cores)

self._patroni.ensure_slots_controller_by_patroni(replication_slots)

Expand Down
24 changes: 20 additions & 4 deletions src/patroni.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,15 +459,26 @@ def member_streaming(self) -> bool:
return r.json().get("replication_state") == "streaming"

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def bulk_update_parameters_controller_by_patroni(self, parameters: dict[str, Any]) -> None:
def bulk_update_parameters_controller_by_patroni(
self, parameters: dict[str, Any], base_parameters: dict[str, Any] | None
) -> None:
"""Update the value of a parameter controller by Patroni.

For more information, check https://patroni.readthedocs.io/en/latest/patroni_configuration.html#postgresql-parameters-controlled-by-patroni.
"""
if not base_parameters:
base_parameters = {}
requests.patch(
f"{self._patroni_url}/config",
verify=self._verify,
json={"postgresql": {"parameters": parameters}},
json={
"postgresql": {
"remove_data_directory_on_rewind_failure": False,
"remove_data_directory_on_diverged_timelines": False,
"parameters": parameters,
},
**base_parameters,
},
auth=self._patroni_auth,
timeout=PATRONI_TIMEOUT,
)
Expand Down Expand Up @@ -685,15 +696,20 @@ def restart_postgresql(self) -> None:
timeout=PATRONI_TIMEOUT,
)

def switchover(self, candidate: str | None = None, wait: bool = True) -> None:
def switchover(
self, candidate: str | None = None, wait: bool = True, async_cluster: bool = False
) -> None:
"""Trigger a switchover."""
# Try to trigger the switchover.
if candidate is not None:
candidate = candidate.replace("/", "-")

for attempt in Retrying(stop=stop_after_delay(60), wait=wait_fixed(3)):
with attempt:
primary = self.get_primary()
primary = self.get_primary() if not async_cluster else self.get_standby_leader()
if primary == candidate:
logger.info("Candidate and leader are the same")
return
r = requests.post(
f"{self._patroni_url}/switchover",
json={"leader": primary, "candidate": candidate},
Expand Down
17 changes: 2 additions & 15 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,28 +104,15 @@ def juju(request: pytest.FixtureRequest):

This adds command line parameter ``--keep-models`` (see help for details).
"""
controller = request.config.getoption("--controller")
model = request.config.getoption("--model")
controller_and_model = None
if controller and model:
controller_and_model = f"{controller}:{model}"
elif controller:
controller_and_model = controller
elif model:
controller_and_model = model
keep_models = bool(request.config.getoption("--keep-models"))

if controller_and_model:
juju = jubilant.Juju(model=controller_and_model) # type: ignore
if model:
juju = jubilant.Juju(model=model)
yield juju
log = juju.debug_log(limit=1000)
else:
with jubilant.temp_model(keep=keep_models) as juju:
yield juju
log = juju.debug_log(limit=1000)

if request.session.testsfailed:
print(log, end="")


@pytest.fixture(scope="module")
Expand Down
Loading
Loading