Skip to content

Commit 59ccbb4

Browse files
authored
port experimental max_connections (#425)
1 parent 38d5709 commit 59ccbb4

File tree

4 files changed

+57
-5
lines changed

4 files changed

+57
-5
lines changed

config.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,11 @@ options:
3636
mysql-root-interface-database:
3737
description: "The database name for the legacy 'mysql' interface (root level access)"
3838
type: "string"
39+
# Experimental features
40+
experimental-max-connections:
41+
type: int
42+
description: |
43+
Maximum number of connections allowed to the MySQL server.
44+
When set max-connections value take precedence over the memory utilizations
45+
againts innodb_buffer_pool_size.
46+
This is an experimental feature and may be removed in future releases.

lib/charms/mysql/v0/mysql.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def wait_until_mysql_connection(self) -> None:
130130

131131
# Increment this PATCH version before using `charmcraft publish-lib` or reset
132132
# to 0 if you are raising the major API version
133-
LIBPATCH = 59
133+
LIBPATCH = 60
134134

135135
UNIT_TEARDOWN_LOCKNAME = "unit-teardown"
136136
UNIT_ADD_LOCKNAME = "unit-add"
@@ -141,7 +141,8 @@ def wait_until_mysql_connection(self) -> None:
141141
BYTES_1MiB = 1048576 # 1 mebibyte
142142
RECOVERY_CHECK_TIME = 10 # seconds
143143
GET_MEMBER_STATE_TIME = 10 # seconds
144-
MIN_MAX_CONNECTIONS = 100
144+
MAX_CONNECTIONS_FLOOR = 10
145+
MIM_MEM_BUFFERS = 200 * BYTES_1MiB
145146

146147
SECRET_INTERNAL_LABEL = "secret-id"
147148
SECRET_DELETED_LABEL = "None"
@@ -868,36 +869,60 @@ def render_mysqld_configuration(
868869
*,
869870
profile: str,
870871
memory_limit: Optional[int] = None,
872+
experimental_max_connections: Optional[int] = None,
871873
snap_common: str = "",
872874
) -> tuple[str, dict]:
873875
"""Render mysqld ini configuration file.
874876
875877
Args:
876878
profile: profile to use for the configuration (testing, production)
877879
memory_limit: memory limit to use for the configuration in bytes
880+
experimental_max_connections: explicit max connections to use for the configuration
878881
snap_common: snap common directory (for log files locations in vm)
879882
880883
Returns: a tuple with mysqld ini file string content and a the config dict
881884
"""
885+
max_connections = None
882886
performance_schema_instrument = ""
883887
if profile == "testing":
884888
innodb_buffer_pool_size = 20 * BYTES_1MiB
885889
innodb_buffer_pool_chunk_size = 1 * BYTES_1MiB
886890
group_replication_message_cache_size = 128 * BYTES_1MiB
887-
max_connections = MIN_MAX_CONNECTIONS
891+
max_connections = 100
888892
performance_schema_instrument = "'memory/%=OFF'"
889893
else:
890894
available_memory = self.get_available_memory()
891895
if memory_limit:
892896
# when memory limit is set, we need to use the minimum
893897
# between the available memory and the limit
894898
available_memory = min(available_memory, memory_limit)
899+
900+
if experimental_max_connections:
901+
# when set, we use the experimental max connections
902+
# and it takes precedence over buffers usage
903+
max_connections = experimental_max_connections
904+
# we reserve 200MiB for memory buffers
905+
# even when there's some overcommittment
906+
available_memory = max(
907+
available_memory - max_connections * 12 * BYTES_1MiB, 200 * BYTES_1MiB
908+
)
909+
895910
(
896911
innodb_buffer_pool_size,
897912
innodb_buffer_pool_chunk_size,
898913
group_replication_message_cache_size,
899914
) = self.get_innodb_buffer_pool_parameters(available_memory)
900-
max_connections = max(self.get_max_connections(available_memory), MIN_MAX_CONNECTIONS)
915+
916+
# constrain max_connections based on the available memory
917+
# after innodb_buffer_pool_size calculation
918+
available_memory -= innodb_buffer_pool_size + (
919+
group_replication_message_cache_size or 0
920+
)
921+
if not max_connections:
922+
max_connections = max(
923+
self.get_max_connections(available_memory), MAX_CONNECTIONS_FLOOR
924+
)
925+
901926
if available_memory < 2 * BYTES_1GiB:
902927
# disable memory instruments if we have less than 2GiB of RAM
903928
performance_schema_instrument = "'memory/%=OFF'"

src/charm.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,9 @@ def _restart(self, event: EventBase) -> None:
373373
restart_states = {
374374
self.restart_peers.data[unit].get("state", "unset") for unit in self.peers.units
375375
}
376-
if restart_states != {"release"}:
376+
if restart_states == {"unset"}:
377+
logger.info("Restarting primary")
378+
elif restart_states != {"release"}:
377379
# Wait other units restart first to minimize primary switchover
378380
message = "Primary restart deferred after other units"
379381
logger.info(message)
@@ -384,6 +386,8 @@ def _restart(self, event: EventBase) -> None:
384386
container = self.unit.get_container(CONTAINER_NAME)
385387
if container.can_connect():
386388
container.restart(MYSQLD_SAFE_SERVICE)
389+
sleep(10)
390+
self._on_update_status(None)
387391

388392
# =========================================================================
389393
# Charm event handlers
@@ -440,6 +444,7 @@ def _on_config_changed(self, event: EventBase) -> None:
440444
new_config_content, new_config_dict = self._mysql.render_mysqld_configuration(
441445
profile=self.config.profile,
442446
memory_limit=memory_limit_bytes,
447+
experimental_max_connections=self.config.experimental_max_connections,
443448
)
444449

445450
changed_config = compare_dictionaries(previous_config_dict, new_config_dict)
@@ -506,6 +511,7 @@ def _write_mysqld_configuration(self):
506511
new_config_content, _ = self._mysql.render_mysqld_configuration(
507512
profile=self.config.profile,
508513
memory_limit=memory_limit_bytes,
514+
experimental_max_connections=self.config.experimental_max_connections,
509515
)
510516
self._mysql.write_content_to_file(path=MYSQLD_CONFIG_FILE, content=new_config_content)
511517

src/config.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import Optional
1010

1111
from charms.data_platform_libs.v0.data_models import BaseConfigModel
12+
from charms.mysql.v0.mysql import MAX_CONNECTIONS_FLOOR
1213
from pydantic import validator
1314

1415
logger = logging.getLogger(__name__)
@@ -53,6 +54,7 @@ class CharmConfig(BaseConfigModel):
5354
mysql_interface_database: Optional[str]
5455
mysql_root_interface_user: Optional[str]
5556
mysql_root_interface_database: Optional[str]
57+
experimental_max_connections: Optional[int]
5658

5759
@validator("profile")
5860
@classmethod
@@ -116,3 +118,14 @@ def database_name_validator(cls, value: str) -> Optional[str]:
116118
)
117119

118120
return value
121+
122+
@validator("experimental_max_connections")
123+
@classmethod
124+
def experimental_max_connections_validator(cls, value: int) -> Optional[int]:
125+
"""Check experimental max connections."""
126+
if value < MAX_CONNECTIONS_FLOOR:
127+
raise ValueError(
128+
f"experimental-max-connections must be greater than {MAX_CONNECTIONS_FLOOR}"
129+
)
130+
131+
return value

0 commit comments

Comments
 (0)