/
githubmirror
/
salt
Обзор
Документация
Войти
/
githubmirror
/
salt
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
tests/pytests/perf/test_cache_benchmarks.py
298 строк
9 KB
Daniel A. Wozniak
Evict mmap_cache registry when tuning opts change
05 май 2026, 19:55
05 май 2026, 19:55
1126580
Код
Авторство
О чём код?
""" Performance benchmarks: mmap_cache vs localfs. Run with:: pytest tests/pytests/perf/test_cache_benchmarks.py -v \ --benchmark-columns=mean,stddev,median,ops,rounds \ --benchmark-sort=name These tests are *not* collected by the regular test suite (``tests/`` root is scanned only for ``unit/``, ``functional/``, etc.). They must be invoked explicitly. Benchmark matrix ---------------- Each operation (store, fetch, updated, flush-key, list) is benchmarked against both backends with a realistic payload (a dict that mimics a minion grain record, ~1 KB serialised). A second "large payload" variant (~64 KB) tests the heap-append path that only mmap_cache exercises. The ``--benchmark-compare`` flag can be used across runs to track regressions. """ import os import shutil import tempfile import pytest import salt.cache.localfs as localfs import salt.cache.mmap_cache as mmap_cache # --------------------------------------------------------------------------- # Shared fixtures and helpers # --------------------------------------------------------------------------- # Realistic grain-like payload (~1 KB serialised) _SMALL_PAYLOAD = { "os": "Ubuntu", "os_family": "Debian", "osrelease": "22.04", "oscodename": "jammy", "kernel": "Linux", "kernelrelease": "5.15.0-91-generic", "cpuarch": "x86_64", "num_cpus": 8, "mem_total": 32768, "ip_interfaces": {"eth0": ["10.0.0.1"], "lo": ["127.0.0.1"]}, "fqdn": "minion-001.example.com", "id": "minion-001", "saltversion": "3009.0", "pythonversion": [3, 11, 0, "final", 0], "grains_cache_enabled": True, "roles": ["web", "cache", "worker"], } # Large payload (~64 KB) — stresses heap-append vs file-per-key _LARGE_PAYLOAD = { "big_data": "x" * 65_000, "id": "minion-big", } _BANK = "minions" _KEY = "grains" _N_KEYS = 200 # number of distinct keys for list / bulk tests _OPTS = { # Size is set to _N_KEYS * 10 (~10 % fill factor). In production it # would be tuned to the expected bank cardinality; keeping it proportional # here ensures list/scan benchmarks reflect realistic index utilisation # rather than an artificially sparse table. "mmap_cache_size": _N_KEYS * 10, "mmap_cache_slot_size": 96, "mmap_cache_key_size": 64, } # When this module runs inside a full integration session, ``mmap_cache`` may # already have ``__opts__`` from the live master harness (tiny mmap tables). # Force loader-module wiring so benchmarks always see *_OPTS above. pytestmark = pytest.mark.usefixtures("configure_loader_modules") @pytest.fixture def configure_loader_modules(): return { localfs: {}, mmap_cache: {"__opts__": _OPTS}, } @pytest.fixture def localfs_dir(tmp_path): d = str(tmp_path / "localfs") os.makedirs(d) yield d shutil.rmtree(d, ignore_errors=True) @pytest.fixture def mmap_dir(tmp_path, configure_loader_modules): d = str(tmp_path / "mmap") os.makedirs(d) mmap_cache._caches.clear() yield d mmap_cache._caches.clear() shutil.rmtree(d, ignore_errors=True) @pytest.fixture def localfs_populated(localfs_dir): """localfs cache pre-populated with _N_KEYS entries.""" for i in range(_N_KEYS): localfs.store(_BANK, f"key_{i:04d}", _SMALL_PAYLOAD, cachedir=localfs_dir) return localfs_dir @pytest.fixture def mmap_populated(mmap_dir): """mmap_cache pre-populated with _N_KEYS entries.""" for i in range(_N_KEYS): mmap_cache.store(_BANK, f"key_{i:04d}", _SMALL_PAYLOAD, cachedir=mmap_dir) return mmap_dir # --------------------------------------------------------------------------- # store — small payload # --------------------------------------------------------------------------- def test_localfs_store_small(benchmark, localfs_dir): benchmark(localfs.store, _BANK, _KEY, _SMALL_PAYLOAD, cachedir=localfs_dir) def test_mmap_store_small(benchmark, mmap_dir): benchmark(mmap_cache.store, _BANK, _KEY, _SMALL_PAYLOAD, cachedir=mmap_dir) # --------------------------------------------------------------------------- # store — large payload # --------------------------------------------------------------------------- def test_localfs_store_large(benchmark, localfs_dir): benchmark(localfs.store, _BANK, _KEY, _LARGE_PAYLOAD, cachedir=localfs_dir) def test_mmap_store_large(benchmark, mmap_dir): benchmark(mmap_cache.store, _BANK, _KEY, _LARGE_PAYLOAD, cachedir=mmap_dir) # --------------------------------------------------------------------------- # fetch (cache warm — repeated reads of same key) # --------------------------------------------------------------------------- def test_localfs_fetch_warm(benchmark, localfs_dir): localfs.store(_BANK, _KEY, _SMALL_PAYLOAD, cachedir=localfs_dir) benchmark(localfs.fetch, _BANK, _KEY, cachedir=localfs_dir) def test_mmap_fetch_warm(benchmark, mmap_dir): mmap_cache.store(_BANK, _KEY, _SMALL_PAYLOAD, cachedir=mmap_dir) benchmark(mmap_cache.fetch, _BANK, _KEY, cachedir=mmap_dir) # --------------------------------------------------------------------------- # fetch — large payload # --------------------------------------------------------------------------- def test_localfs_fetch_large(benchmark, localfs_dir): localfs.store(_BANK, _KEY, _LARGE_PAYLOAD, cachedir=localfs_dir) benchmark(localfs.fetch, _BANK, _KEY, cachedir=localfs_dir) def test_mmap_fetch_large(benchmark, mmap_dir): mmap_cache.store(_BANK, _KEY, _LARGE_PAYLOAD, cachedir=mmap_dir) benchmark(mmap_cache.fetch, _BANK, _KEY, cachedir=mmap_dir) # --------------------------------------------------------------------------- # updated (mtime lookup — mmap reads index only, no heap) # --------------------------------------------------------------------------- def test_localfs_updated(benchmark, localfs_dir): localfs.store(_BANK, _KEY, _SMALL_PAYLOAD, cachedir=localfs_dir) benchmark(localfs.updated, _BANK, _KEY, cachedir=localfs_dir) def test_mmap_updated(benchmark, mmap_dir): mmap_cache.store(_BANK, _KEY, _SMALL_PAYLOAD, cachedir=mmap_dir) benchmark(mmap_cache.updated, _BANK, _KEY, cachedir=mmap_dir) # --------------------------------------------------------------------------- # list — N keys already stored # --------------------------------------------------------------------------- def test_localfs_list(benchmark, localfs_populated): benchmark(localfs.list_, _BANK, cachedir=localfs_populated) def test_mmap_list(benchmark, mmap_populated): benchmark(mmap_cache.list_, _BANK, cachedir=mmap_populated) # --------------------------------------------------------------------------- # flush (delete a single key) # --------------------------------------------------------------------------- def test_localfs_flush_key(benchmark, localfs_dir): def setup(): localfs.store(_BANK, _KEY, _SMALL_PAYLOAD, cachedir=localfs_dir) def run(): localfs.flush(_BANK, key=_KEY, cachedir=localfs_dir) benchmark.pedantic(run, setup=setup, rounds=200, warmup_rounds=5) def test_mmap_flush_key(benchmark, mmap_dir): def setup(): mmap_cache.store(_BANK, _KEY, _SMALL_PAYLOAD, cachedir=mmap_dir) def run(): mmap_cache.flush_(_BANK, key=_KEY, cachedir=mmap_dir) benchmark.pedantic(run, setup=setup, rounds=200, warmup_rounds=5) # --------------------------------------------------------------------------- # sequential append (Raft hot-path simulation) # # Appends N entries to a log bank in sequence. This is the primary use-case # where mmap_cache should show the largest advantage: each append is a single # heap write + index slot update, with no directory entry creation. # --------------------------------------------------------------------------- def test_localfs_sequential_append(benchmark, localfs_dir): counter = [0] def append_one(): k = f"{counter[0]:08d}" counter[0] += 1 localfs.store( "cluster/raft/log", k, {"term": 1, "entry": k}, cachedir=localfs_dir ) benchmark.pedantic(append_one, rounds=500, warmup_rounds=10) def test_mmap_sequential_append(benchmark, mmap_dir): counter = [0] def append_one(): k = f"{counter[0]:08d}" counter[0] += 1 mmap_cache.store( "cluster/raft/log", k, {"term": 1, "entry": k}, cachedir=mmap_dir ) benchmark.pedantic(append_one, rounds=500, warmup_rounds=10) # --------------------------------------------------------------------------- # bulk store + full scan (simulates startup log recovery) # --------------------------------------------------------------------------- def test_localfs_bulk_store_and_scan(benchmark, localfs_dir): def run(): d = tempfile.mkdtemp(dir=localfs_dir) try: for i in range(50): localfs.store("log", f"{i:04d}", {"i": i}, cachedir=d) return localfs.list_("log", cachedir=d) finally: shutil.rmtree(d, ignore_errors=True) benchmark(run) def test_mmap_bulk_store_and_scan(benchmark, tmp_path, configure_loader_modules): def run(): d = str(tmp_path / f"mmap_bulk_{run.counter}") run.counter += 1 os.makedirs(d, exist_ok=True) mmap_cache._caches.clear() for i in range(50): mmap_cache.store("log", f"{i:04d}", {"i": i}, cachedir=d) return mmap_cache.list_("log", cachedir=d) run.counter = 0 benchmark(run)