Skip to content

Commit

Permalink
fix(nemesis): fix default time to live setter according scylla state
Browse files Browse the repository at this point in the history
default time to live being set according to current table states
or set acceptable default value
  • Loading branch information
timtimb0t authored and temichus committed Nov 25, 2024
1 parent 2875ad0 commit 481ebee
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 3 deletions.
37 changes: 34 additions & 3 deletions sdcm/nemesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@
QuotaConfigurationFailure,
)
from test_lib.compaction import CompactionStrategy, get_compaction_strategy, get_compaction_random_additional_params, \
get_gc_mode, GcMode
get_gc_mode, GcMode, calculate_allowed_twcs_ttl_borders, get_table_compaction_info
from test_lib.cql_types import CQLTypeBuilder
from test_lib.sla import ServiceLevel, MAX_ALLOWED_SERVICE_LEVELS
from sdcm.utils.topology_ops import FailedDecommissionOperationMonitoring
Expand Down Expand Up @@ -2723,6 +2723,10 @@ def modify_table_compaction(self):
},
]
prop_val = random.choice(strategies)
# max allowed TTL - 49 days (4300000) (to be compatible with default TWCS settings)
if prop_val['class'] == 'TimeWindowCompactionStrategy':
self._modify_table_property(name="default_time_to_live", val=str(4300000))

self._modify_table_property(name="compaction", val=str(prop_val))

def modify_table_compression(self):
Expand Down Expand Up @@ -2763,15 +2767,42 @@ def modify_table_default_time_to_live(self): # pylint: disable=invalid-name
The value of this property is a number of seconds. If it is set, Cassandra applies a
default TTL marker to each column in the table, set to this value. When the table TTL
is exceeded, Cassandra tombstones the table.
This nemesis selects random table, check if it has TimeWindowCompactionStrategy applied
and calculate possible default time to live, if no - sets random values in allowed range.
default: default_time_to_live = 0
"""
# Select table without columns with "counter" type for this nemesis - issue #1037:
# Modify_table nemesis chooses first non-system table, and modify default_time_to_live of it.
# But table with counters doesn't support this

# max allowed TTL - 49 days (4300000) (to be compatible with default TWCS settings)
self._modify_table_property(name="default_time_to_live", val=random.randint(864000, 4300000),
filter_out_table_with_counter=True)

default_min_ttl = 864000 # 10 days in seconds
default_max_ttl = 4300000

ks_cfs = self.cluster.get_non_system_ks_cf_list(
db_node=self.target_node, filter_out_table_with_counter=True,
filter_out_mv=True)

keyspace_table = random.choice(ks_cfs) if ks_cfs else ks_cfs
keyspace, table = keyspace_table.split('.')
compaction_strategy = get_compaction_strategy(node=self.target_node, keyspace=keyspace, table=table)

if compaction_strategy == CompactionStrategy.TIME_WINDOW:
with self.cluster.cql_connection_patient(self.target_node) as session:
LOGGER.debug(f'Getting data from Scylla node: {self.target_node}, table: {keyspace_table}')
compaction_properties = get_table_compaction_info(
keyspace=keyspace, table=table, session=session
)
default_min_ttl, default_max_ttl = calculate_allowed_twcs_ttl_borders(
compaction_properties, default_min_ttl
)

value = random.randint(default_min_ttl, default_max_ttl)

InfoEvent(f'New default time to live to be set: {value}, for table: {keyspace_table}').publish()
self._modify_table_property(name="default_time_to_live", val=value,
filter_out_table_with_counter=True, keyspace_table=keyspace_table)

def modify_table_max_index_interval(self):
"""
Expand Down
64 changes: 64 additions & 0 deletions test_lib/compaction.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import logging
import random
from dataclasses import dataclass, fields
from enum import Enum
from typing import Optional

import yaml

Expand Down Expand Up @@ -31,6 +33,21 @@ def from_str(cls, output_str):
raise ValueError(err_msg) from attr_err


@dataclass
class TimeWindowCompactionProperties:
class_name: str = ''
compaction_window_unit: Optional[str] = None
compaction_window_size: Optional[int] = None
expired_sstable_check_frequency_seconds: Optional[int] = None
min_threshold: Optional[int] = None
max_threshold: Optional[int] = None

@classmethod
def from_dict(cls, data: dict, **kwargs):
field_names = [field.name for field in fields(cls)]
return cls(**{key: val for key, val in data.items() if key in field_names}, **kwargs)


def get_gc_mode(node: BaseNode, keyspace: str, table: str) -> str | GcMode:
"""Get a given table GC mode
Expand Down Expand Up @@ -85,6 +102,21 @@ def get_compaction_strategy(node, keyspace, table):
return compaction


def get_table_compaction_info(keyspace: str, table: str, session: object):

query = f"SELECT compaction FROM system_schema.tables WHERE keyspace_name = '{keyspace}' AND table_name = '{table}';"
result = session.execute(query).one()
LOGGER.debug(f"Query result for {keyspace}.{table} compaction is: {result}")

if result and result.compaction:
compaction_dict = result.compaction
compaction_properties = TimeWindowCompactionProperties.from_dict(data=compaction_dict)
else:
compaction_properties = TimeWindowCompactionProperties(class_name='')

return compaction_properties


def get_compaction_random_additional_params(strategy: CompactionStrategy):
"""
Expand All @@ -110,3 +142,35 @@ def get_compaction_random_additional_params(strategy: CompactionStrategy):
{'sstable_size_in_mb': sstable_size_in_mb}]
}
return list_additional_params_options[strategy]


def calculate_allowed_twcs_ttl_borders(compaction_info: TimeWindowCompactionProperties, default_min_ttl: int):

compaction_window_size = compaction_info.compaction_window_size or 1
compaction_window_unit = compaction_info.compaction_window_unit or 'DAYS'
LOGGER.debug(f'Compaction window size: {compaction_window_size}, Unit: {compaction_window_unit}')

# Convert compaction_window_size to seconds
unit_multipliers = {
'MINUTES': 60,
'HOURS': 3600,
'DAYS': 86400,
'WEEKS': 604800,
}
multiplier = unit_multipliers.get(compaction_window_unit.upper(), 86400)
compaction_window_size_seconds = int(compaction_window_size) * multiplier

# twcs_max_window_count default value is 50; adjust as necessary
# opensource.docs.scylladb.com/stable/reference/configuration-parameters.html
twcs_max_window_count = random.randint(10, 45)

# Calculate maximum allowed default_time_to_live
calculated_max_ttl = twcs_max_window_count * compaction_window_size_seconds
LOGGER.debug(f'Maximum allowed default_time_to_live: {calculated_max_ttl} seconds')

# Check if max_ttl is less than min_ttl
if calculated_max_ttl < default_min_ttl:
# If calculated max_ttl < default_min_ttl, return a range from (max_ttl // 3) to max_ttl
return calculated_max_ttl // 3, calculated_max_ttl
else:
return default_min_ttl, calculated_max_ttl

0 comments on commit 481ebee

Please sign in to comment.