From 257ebbeca9e3c0be6a64fbda03788ffceba1d677 Mon Sep 17 00:00:00 2001 From: Nikos Dragazis Date: Tue, 15 Jul 2025 21:08:20 +0300 Subject: [PATCH] test: Use in-memory SQLite for PyKMIP server The PyKMIP server uses an SQLite database to store artifacts such as encryption keys. By default, SQLite performs a full journal and data flush to disk on every CREATE TABLE operation. Each operation triggers three fdatasync(2) calls. If we multiply this by 16, that is the number of tables created by the server, we get a significant number of file syncs, which can last for several seconds on slow machines. This behavior has led to CI stability issues from KMIP unit tests where the server failed to complete its schema creation within the 20-second timeout (observed on spider9 and spider11). Fix this by configuring the server to use an in-memory SQLite. Fixes #24842. Signed-off-by: Nikos Dragazis Closes scylladb/scylladb#24995 (cherry picked from commit 2656fca504da4175b22ff62ea3158dd89fe0fade) Closes scylladb/scylladb#25300 --- test/boost/encryption_at_rest_test.cc | 4 ++-- test/boost/kmip_wrapper.py | 34 ++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/test/boost/encryption_at_rest_test.cc b/test/boost/encryption_at_rest_test.cc index 1d3eb64fe7..7de0ff261f 100644 --- a/test/boost/encryption_at_rest_test.cc +++ b/test/boost/encryption_at_rest_test.cc @@ -358,8 +358,8 @@ auth_suite=TLS1.2 policy_path={} enable_tls_client_auth=False logging_level=DEBUG -database_path={}/pykmip.db - )foo", info.cert, info.key, info.ca, tmp.path().string(), tmp.path().string()); +database_path=:memory: + )foo", info.cert, info.key, info.ca, tmp.path().string()); auto cfgfile = fmt::format("{}/pykmip.conf", tmp.path().string()); auto log = fmt::format("{}/pykmip.log", tmp.path().string()); diff --git a/test/boost/kmip_wrapper.py b/test/boost/kmip_wrapper.py index b6deee255e..85d6486229 100644 --- a/test/boost/kmip_wrapper.py +++ b/test/boost/kmip_wrapper.py @@ -1,13 +1,39 @@ import ssl import sys +import functools + +import sqlalchemy +from sqlalchemy.pool import StaticPool from kmip.services import auth from kmip.services.server.server import build_argument_parser from kmip.services.server.server import KmipServer -# Helper wrapper for running pykmip in scylla testing. Needed because TLS options -# (hardcoded) in pykmip are obsolete and will not work with connecting using gnutls -# of any modern variety. +# Helper wrapper for running pykmip in scylla testing. Needed for the following +# reasons: +# +# * TLS options (hardcoded) in pykmip are obsolete and will not work with +# connecting using gnutls of any modern variety. +# +# * We need to use an in-memory SQLite database for testing (file-based SQLite +# issues many fdatasync's which have been proven to reduce CI stability in +# some slow machines). An in-memory SQLite database is bound to a single +# connection. In order to share it among multiple threads, the connection +# itself must be shared. We achieve that with the StaticPool. +# https://docs.sqlalchemy.org/en/20/dialects/sqlite.html#using-a-memory-database-in-multiple-threads + + +def monkey_patch_create_engine(): + original_create_engine = sqlalchemy.create_engine + + @functools.wraps(original_create_engine) + def patched_create_engine(*args, **kwargs): + if args and isinstance(args[0], str) and args[0].startswith('sqlite:///:memory:'): + kwargs['poolclass'] = StaticPool + return original_create_engine(*args, **kwargs) + + sqlalchemy.create_engine = patched_create_engine + class TLS13AuthenticationSuite(auth.TLS12AuthenticationSuite): """ @@ -60,6 +86,8 @@ def main(): kwargs['live_policies'] = True + monkey_patch_create_engine() + # Create and start the server. s = KmipServer(**kwargs) # Fix TLS. Try to get this into mainline project, but that will take time...