redis-benchmarks-specification 0.1.309__py3-none-any.whl → 0.1.310__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of redis-benchmarks-specification might be problematic. Click here for more details.

Files changed (20) hide show
  1. redis_benchmarks_specification/__cli__/stats.py +20 -11
  2. redis_benchmarks_specification/__common__/runner.py +13 -9
  3. redis_benchmarks_specification/__common__/timeseries.py +3 -3
  4. redis_benchmarks_specification/__compare__/args.py +1 -3
  5. redis_benchmarks_specification/__compare__/compare.py +19 -8
  6. redis_benchmarks_specification/__runner__/args.py +3 -3
  7. redis_benchmarks_specification/__runner__/remote_profiling.py +3 -1
  8. redis_benchmarks_specification/__runner__/runner.py +305 -116
  9. redis_benchmarks_specification/__self_contained_coordinator__/docker.py +6 -1
  10. redis_benchmarks_specification/__self_contained_coordinator__/prepopulation.py +2 -0
  11. redis_benchmarks_specification/__self_contained_coordinator__/runners.py +7 -2
  12. redis_benchmarks_specification/__self_contained_coordinator__/self_contained_coordinator.py +9 -5
  13. redis_benchmarks_specification/test-suites/memtier_benchmark-1Mkeys-100B-expire-use-case.yml +2 -2
  14. redis_benchmarks_specification/test-suites/memtier_benchmark-playbook-session-storage-100k-sessions.yml +3 -2
  15. redis_benchmarks_specification/test-suites/memtier_benchmark-playbook-session-storage-1k-sessions.yml +3 -2
  16. {redis_benchmarks_specification-0.1.309.dist-info → redis_benchmarks_specification-0.1.310.dist-info}/METADATA +3 -2
  17. {redis_benchmarks_specification-0.1.309.dist-info → redis_benchmarks_specification-0.1.310.dist-info}/RECORD +20 -20
  18. {redis_benchmarks_specification-0.1.309.dist-info → redis_benchmarks_specification-0.1.310.dist-info}/WHEEL +1 -1
  19. {redis_benchmarks_specification-0.1.309.dist-info → redis_benchmarks_specification-0.1.310.dist-info}/LICENSE +0 -0
  20. {redis_benchmarks_specification-0.1.309.dist-info → redis_benchmarks_specification-0.1.310.dist-info}/entry_points.txt +0 -0
@@ -250,7 +250,7 @@ def detect_object_encoding(redis_conn, dbconfig):
250
250
  "confidence": 0.0,
251
251
  "sample_size": 0,
252
252
  "total_keys": 0,
253
- "encoding_distribution": {}
253
+ "encoding_distribution": {},
254
254
  }
255
255
 
256
256
  # Determine scanning strategy based on dataset size
@@ -258,12 +258,16 @@ def detect_object_encoding(redis_conn, dbconfig):
258
258
  # For small datasets, scan all keys for complete accuracy
259
259
  sample_size = total_keys
260
260
  scan_all_keys = True
261
- logging.info(f"Scanning all {total_keys} keys (small dataset - complete analysis)")
261
+ logging.info(
262
+ f"Scanning all {total_keys} keys (small dataset - complete analysis)"
263
+ )
262
264
  else:
263
265
  # For large datasets, sample 1% (minimum 10, maximum 1000)
264
266
  sample_size = max(10, min(1000, int(total_keys * 0.01)))
265
267
  scan_all_keys = False
266
- logging.info(f"Sampling {sample_size} keys out of {total_keys} total keys ({(sample_size/total_keys)*100:.2f}%)")
268
+ logging.info(
269
+ f"Sampling {sample_size} keys out of {total_keys} total keys ({(sample_size/total_keys)*100:.2f}%)"
270
+ )
267
271
 
268
272
  # Use SCAN to get keys
269
273
  encoding_counts = {}
@@ -282,7 +286,9 @@ def detect_object_encoding(redis_conn, dbconfig):
282
286
  else:
283
287
  # Sample keys until we reach our target sample size
284
288
  while len(scanned_keys) < sample_size:
285
- cursor, keys = redis_conn.scan(cursor=cursor, count=min(100, sample_size - len(scanned_keys)))
289
+ cursor, keys = redis_conn.scan(
290
+ cursor=cursor, count=min(100, sample_size - len(scanned_keys))
291
+ )
286
292
  scanned_keys.extend(keys)
287
293
 
288
294
  # Break if we've completed a full scan
@@ -292,25 +298,35 @@ def detect_object_encoding(redis_conn, dbconfig):
292
298
  # Limit to our target sample size
293
299
  scanned_keys = scanned_keys[:sample_size]
294
300
 
295
- logging.debug(f"SCAN completed: found {len(scanned_keys)} keys, cursor ended at {cursor}")
301
+ logging.debug(
302
+ f"SCAN completed: found {len(scanned_keys)} keys, cursor ended at {cursor}"
303
+ )
296
304
 
297
305
  # If SCAN didn't find any keys but we know there are keys, try KEYS command as fallback
298
306
  if len(scanned_keys) == 0 and total_keys > 0:
299
- logging.warning(f"SCAN found no keys but DBSIZE reports {total_keys} keys. Trying KEYS fallback.")
307
+ logging.warning(
308
+ f"SCAN found no keys but DBSIZE reports {total_keys} keys. Trying KEYS fallback."
309
+ )
300
310
  try:
301
311
  # Use KEYS * as fallback (only for small datasets to avoid blocking)
302
312
  if total_keys <= 1000:
303
- all_keys = redis_conn.keys('*')
304
- scanned_keys = all_keys[:sample_size] if not scan_all_keys else all_keys
313
+ all_keys = redis_conn.keys("*")
314
+ scanned_keys = (
315
+ all_keys[:sample_size] if not scan_all_keys else all_keys
316
+ )
305
317
  logging.info(f"KEYS fallback found {len(scanned_keys)} keys")
306
318
  else:
307
- logging.error(f"Cannot use KEYS fallback for large dataset ({total_keys} keys)")
319
+ logging.error(
320
+ f"Cannot use KEYS fallback for large dataset ({total_keys} keys)"
321
+ )
308
322
  except Exception as e:
309
323
  logging.error(f"KEYS fallback failed: {e}")
310
324
 
311
325
  # Final check: if we still have no keys, return early
312
326
  if len(scanned_keys) == 0:
313
- logging.error(f"No keys found for encoding detection despite DBSIZE={total_keys}")
327
+ logging.error(
328
+ f"No keys found for encoding detection despite DBSIZE={total_keys}"
329
+ )
314
330
  return {
315
331
  "encoding": "unknown",
316
332
  "confidence": 0.0,
@@ -318,7 +334,7 @@ def detect_object_encoding(redis_conn, dbconfig):
318
334
  "total_keys": total_keys,
319
335
  "encoding_distribution": {},
320
336
  "is_complete_scan": scan_all_keys,
321
- "error": "No keys found by SCAN or KEYS commands"
337
+ "error": "No keys found by SCAN or KEYS commands",
322
338
  }
323
339
 
324
340
  # Get encoding for each sampled key
@@ -328,10 +344,12 @@ def detect_object_encoding(redis_conn, dbconfig):
328
344
  # Use the redis-py object_encoding method instead of raw command
329
345
  encoding = redis_conn.object("ENCODING", key)
330
346
  if isinstance(encoding, bytes):
331
- encoding = encoding.decode('utf-8')
347
+ encoding = encoding.decode("utf-8")
332
348
  elif encoding is None:
333
349
  # Key might have expired or been deleted
334
- logging.debug(f"Key '{key}' returned None encoding (key may have expired)")
350
+ logging.debug(
351
+ f"Key '{key}' returned None encoding (key may have expired)"
352
+ )
335
353
  continue
336
354
 
337
355
  encoding_counts[encoding] = encoding_counts.get(encoding, 0) + 1
@@ -345,17 +363,21 @@ def detect_object_encoding(redis_conn, dbconfig):
345
363
  logging.warning(f"Failed to get encoding for key {key}: {e}")
346
364
  continue
347
365
 
348
- logging.debug(f"Successfully got encoding for {successful_encodings}/{len(scanned_keys)} keys")
366
+ logging.debug(
367
+ f"Successfully got encoding for {successful_encodings}/{len(scanned_keys)} keys"
368
+ )
349
369
 
350
370
  if not encoding_counts:
351
- logging.warning(f"No object encodings detected! Scanned {len(scanned_keys)} keys, successful encodings: {successful_encodings}")
371
+ logging.warning(
372
+ f"No object encodings detected! Scanned {len(scanned_keys)} keys, successful encodings: {successful_encodings}"
373
+ )
352
374
  return {
353
375
  "encoding": "unknown",
354
376
  "confidence": 0.0,
355
377
  "sample_size": 0,
356
378
  "total_keys": total_keys,
357
379
  "encoding_distribution": {},
358
- "is_complete_scan": scan_all_keys
380
+ "is_complete_scan": scan_all_keys,
359
381
  }
360
382
 
361
383
  # Determine dominant encoding
@@ -365,8 +387,7 @@ def detect_object_encoding(redis_conn, dbconfig):
365
387
 
366
388
  # Calculate encoding distribution percentages
367
389
  encoding_distribution = {
368
- enc: (count / total_sampled) * 100
369
- for enc, count in encoding_counts.items()
390
+ enc: (count / total_sampled) * 100 for enc, count in encoding_counts.items()
370
391
  }
371
392
 
372
393
  result = {
@@ -375,11 +396,13 @@ def detect_object_encoding(redis_conn, dbconfig):
375
396
  "sample_size": total_sampled,
376
397
  "total_keys": total_keys,
377
398
  "encoding_distribution": encoding_distribution,
378
- "is_complete_scan": scan_all_keys
399
+ "is_complete_scan": scan_all_keys,
379
400
  }
380
401
 
381
402
  scan_type = "complete scan" if scan_all_keys else "sample"
382
- logging.info(f"Object encoding analysis ({scan_type}): {dominant_encoding[0]} ({confidence*100:.1f}% confidence)")
403
+ logging.info(
404
+ f"Object encoding analysis ({scan_type}): {dominant_encoding[0]} ({confidence*100:.1f}% confidence)"
405
+ )
383
406
  if len(encoding_counts) > 1:
384
407
  logging.info(f"Encoding distribution: {encoding_distribution}")
385
408
 
@@ -393,7 +416,7 @@ def detect_object_encoding(redis_conn, dbconfig):
393
416
  "sample_size": 0,
394
417
  "total_keys": 0,
395
418
  "encoding_distribution": {},
396
- "error": str(e)
419
+ "error": str(e),
397
420
  }
398
421
 
399
422
 
@@ -1285,7 +1308,9 @@ def process_self_contained_coordinator_stream(
1285
1308
  dry_run_count = 0
1286
1309
  dry_run_tests = [] # Track test names for dry run output
1287
1310
  memory_results = [] # Track memory results for memory comparison mode
1288
- loaded_datasets = set() # Track datasets that have been loaded (for memory comparison mode)
1311
+ loaded_datasets = (
1312
+ set()
1313
+ ) # Track datasets that have been loaded (for memory comparison mode)
1289
1314
  dry_run = args.dry_run
1290
1315
  memory_comparison_only = args.memory_comparison_only
1291
1316
  dry_run_include_preload = args.dry_run_include_preload
@@ -1315,7 +1340,11 @@ def process_self_contained_coordinator_stream(
1315
1340
 
1316
1341
  if "dbconfig" in benchmark_config:
1317
1342
  # Skip load tests (keyspacelen = 0) in memory comparison mode
1318
- keyspacelen = benchmark_config["dbconfig"].get("check", {}).get("keyspacelen", None)
1343
+ keyspacelen = (
1344
+ benchmark_config["dbconfig"]
1345
+ .get("check", {})
1346
+ .get("keyspacelen", None)
1347
+ )
1319
1348
  if keyspacelen is not None and keyspacelen == 0:
1320
1349
  logging.debug(f"Skipping load test {test_file} (keyspacelen=0)")
1321
1350
  continue
@@ -1331,8 +1360,12 @@ def process_self_contained_coordinator_stream(
1331
1360
  logging.info(f"Memory comparison mode analysis:")
1332
1361
  logging.info(f" Total tests with datasets: {total_tests_with_datasets}")
1333
1362
  logging.info(f" Unique datasets to load: {len(unique_datasets)}")
1334
- logging.info(f" Dataset ingestion savings: {total_tests_with_datasets - len(unique_datasets)} skipped loads")
1335
- logging.info(f" Load tests skipped: Tests with keyspacelen=0 are automatically excluded")
1363
+ logging.info(
1364
+ f" Dataset ingestion savings: {total_tests_with_datasets - len(unique_datasets)} skipped loads"
1365
+ )
1366
+ logging.info(
1367
+ f" Load tests skipped: Tests with keyspacelen=0 are automatically excluded"
1368
+ )
1336
1369
 
1337
1370
  if len(unique_datasets) > 0:
1338
1371
  logging.info(f" Unique datasets: {', '.join(sorted(unique_datasets))}")
@@ -1420,7 +1453,7 @@ def process_self_contained_coordinator_stream(
1420
1453
  # Auto-detect server information if not explicitly provided
1421
1454
  from redis_benchmarks_specification.__runner__.remote_profiling import (
1422
1455
  extract_server_info_for_args,
1423
- extract_server_metadata_for_timeseries
1456
+ extract_server_metadata_for_timeseries,
1424
1457
  )
1425
1458
 
1426
1459
  detected_info = extract_server_info_for_args(r)
@@ -1431,22 +1464,33 @@ def process_self_contained_coordinator_stream(
1431
1464
  github_repo = args.github_repo
1432
1465
 
1433
1466
  # Auto-detect github_org if it's the default value
1434
- if args.github_org == "redis" and detected_info["github_org"] != "redis":
1467
+ if (
1468
+ args.github_org == "redis"
1469
+ and detected_info["github_org"] != "redis"
1470
+ ):
1435
1471
  github_org = detected_info["github_org"]
1436
1472
  logging.info(f"Auto-detected github_org: {github_org}")
1437
1473
 
1438
1474
  # Auto-detect github_repo if it's the default value
1439
- if args.github_repo == "redis" and detected_info["github_repo"] != "redis":
1475
+ if (
1476
+ args.github_repo == "redis"
1477
+ and detected_info["github_repo"] != "redis"
1478
+ ):
1440
1479
  github_repo = detected_info["github_repo"]
1441
1480
  logging.info(f"Auto-detected github_repo: {github_repo}")
1442
1481
 
1443
1482
  # Auto-detect version if it's the default value
1444
- if args.github_version == "NA" and detected_info["github_version"] != "unknown":
1483
+ if (
1484
+ args.github_version == "NA"
1485
+ and detected_info["github_version"] != "unknown"
1486
+ ):
1445
1487
  git_version = detected_info["github_version"]
1446
1488
  logging.info(f"Auto-detected github_version: {git_version}")
1447
1489
 
1448
1490
  # Auto-detect git hash if it's the default value
1449
- if (git_hash is None or git_hash == "NA") and detected_info["github_hash"] != "unknown":
1491
+ if (git_hash is None or git_hash == "NA") and detected_info[
1492
+ "github_hash"
1493
+ ] != "unknown":
1450
1494
  git_hash = detected_info["github_hash"]
1451
1495
  logging.info(f"Auto-detected git_hash: {git_hash}")
1452
1496
 
@@ -1455,7 +1499,6 @@ def process_self_contained_coordinator_stream(
1455
1499
  tf_github_repo = github_repo
1456
1500
  redis_conns = [r]
1457
1501
 
1458
-
1459
1502
  if oss_cluster_api_enabled:
1460
1503
  redis_conns = []
1461
1504
  logging.info("updating redis connections from cluster slots")
@@ -1499,7 +1542,9 @@ def process_self_contained_coordinator_stream(
1499
1542
  if redis_pid is not None:
1500
1543
  redis_pids.append(redis_pid)
1501
1544
  else:
1502
- logging.warning("Redis process_id not found in INFO command, skipping PID collection for this connection")
1545
+ logging.warning(
1546
+ "Redis process_id not found in INFO command, skipping PID collection for this connection"
1547
+ )
1503
1548
 
1504
1549
  # Check if all tested commands are supported by this Redis instance
1505
1550
  supported_commands = get_supported_redis_commands(redis_conns)
@@ -1549,11 +1594,15 @@ def process_self_contained_coordinator_stream(
1549
1594
  # Send MEMORY PURGE after FLUSHALL for memory comparison mode
1550
1595
  if memory_comparison_only:
1551
1596
  try:
1552
- logging.info("Sending MEMORY PURGE after FLUSHALL at test start")
1597
+ logging.info(
1598
+ "Sending MEMORY PURGE after FLUSHALL at test start"
1599
+ )
1553
1600
  for conn in redis_conns:
1554
1601
  conn.execute_command("MEMORY", "PURGE")
1555
1602
  except Exception as e:
1556
- logging.warning(f"MEMORY PURGE failed after FLUSHALL at test start: {e}")
1603
+ logging.warning(
1604
+ f"MEMORY PURGE failed after FLUSHALL at test start: {e}"
1605
+ )
1557
1606
 
1558
1607
  benchmark_required_memory = get_benchmark_required_memory(
1559
1608
  benchmark_config
@@ -1691,7 +1740,9 @@ def process_self_contained_coordinator_stream(
1691
1740
  continue
1692
1741
 
1693
1742
  # Check if we should skip tests without dataset
1694
- has_dataset = "preload_tool" in benchmark_config.get("dbconfig", {})
1743
+ has_dataset = "preload_tool" in benchmark_config.get(
1744
+ "dbconfig", {}
1745
+ )
1695
1746
  if args.skip_tests_without_dataset is True and not has_dataset:
1696
1747
  logging.warning(
1697
1748
  "Skipping test {} as it does not contain a dataset".format(
@@ -1706,7 +1757,10 @@ def process_self_contained_coordinator_stream(
1706
1757
  continue
1707
1758
 
1708
1759
  # For memory comparison mode, only run tests with dbconfig
1709
- if memory_comparison_only and "dbconfig" not in benchmark_config:
1760
+ if (
1761
+ memory_comparison_only
1762
+ and "dbconfig" not in benchmark_config
1763
+ ):
1710
1764
  logging.warning(
1711
1765
  "Skipping test {} in memory comparison mode as it does not contain dbconfig".format(
1712
1766
  test_name
@@ -1731,16 +1785,22 @@ def process_self_contained_coordinator_stream(
1731
1785
  if "dbconfig" in benchmark_config:
1732
1786
  if "preload_tool" in benchmark_config["dbconfig"]:
1733
1787
  # Check if this dataset has already been loaded (for memory comparison mode)
1734
- dataset_name = benchmark_config["dbconfig"].get("dataset_name")
1788
+ dataset_name = benchmark_config["dbconfig"].get(
1789
+ "dataset_name"
1790
+ )
1735
1791
  skip_preload = False
1736
1792
 
1737
1793
  if memory_comparison_only and dataset_name:
1738
1794
  if dataset_name in loaded_datasets:
1739
- logging.info(f"Skipping preload for dataset '{dataset_name}' - already loaded")
1795
+ logging.info(
1796
+ f"Skipping preload for dataset '{dataset_name}' - already loaded"
1797
+ )
1740
1798
  skip_preload = True
1741
1799
  continue
1742
1800
  else:
1743
- logging.info(f"Loading dataset '{dataset_name}' for the first time")
1801
+ logging.info(
1802
+ f"Loading dataset '{dataset_name}' for the first time"
1803
+ )
1744
1804
  loaded_datasets.add(dataset_name)
1745
1805
 
1746
1806
  if not skip_preload:
@@ -1787,7 +1847,9 @@ def process_self_contained_coordinator_stream(
1787
1847
  # Send MEMORY PURGE before preload for memory comparison mode (if FLUSHALL wasn't already done)
1788
1848
  if memory_comparison_only and not args.flushall_on_every_test_start:
1789
1849
  try:
1790
- logging.info("Sending MEMORY PURGE before preload for memory comparison mode")
1850
+ logging.info(
1851
+ "Sending MEMORY PURGE before preload for memory comparison mode"
1852
+ )
1791
1853
  for conn in redis_conns:
1792
1854
  conn.execute_command("MEMORY", "PURGE")
1793
1855
  except Exception as e:
@@ -1819,12 +1881,20 @@ def process_self_contained_coordinator_stream(
1819
1881
  start_time_ms,
1820
1882
  start_time_str,
1821
1883
  ) = get_start_time_vars()
1822
- dataset_load_duration_seconds = 0 # No dataset loading time for memory comparison
1884
+ dataset_load_duration_seconds = (
1885
+ 0 # No dataset loading time for memory comparison
1886
+ )
1823
1887
 
1824
1888
  # Skip load tests (keyspacelen = 0) in memory comparison mode
1825
- keyspacelen = benchmark_config.get("dbconfig", {}).get("check", {}).get("keyspacelen", None)
1889
+ keyspacelen = (
1890
+ benchmark_config.get("dbconfig", {})
1891
+ .get("check", {})
1892
+ .get("keyspacelen", None)
1893
+ )
1826
1894
  if keyspacelen is not None and keyspacelen == 0:
1827
- logging.info(f"Skipping load test {test_name} in memory comparison mode (keyspacelen=0)")
1895
+ logging.info(
1896
+ f"Skipping load test {test_name} in memory comparison mode (keyspacelen=0)"
1897
+ )
1828
1898
  delete_temporary_files(
1829
1899
  temporary_dir_client=temporary_dir_client,
1830
1900
  full_result_path=None,
@@ -1836,10 +1906,14 @@ def process_self_contained_coordinator_stream(
1836
1906
  if dry_run:
1837
1907
  dry_run_count = dry_run_count + 1
1838
1908
  dry_run_tests.append(test_name)
1839
- logging.info(f"[DRY RUN] Would collect memory stats for test {test_name}")
1909
+ logging.info(
1910
+ f"[DRY RUN] Would collect memory stats for test {test_name}"
1911
+ )
1840
1912
 
1841
1913
  # Add dataset info to dry run output
1842
- dataset_name = benchmark_config.get("dbconfig", {}).get("dataset_name")
1914
+ dataset_name = benchmark_config.get("dbconfig", {}).get(
1915
+ "dataset_name"
1916
+ )
1843
1917
  if dataset_name:
1844
1918
  logging.info(f"[DRY RUN] Dataset: {dataset_name}")
1845
1919
 
@@ -1857,7 +1931,11 @@ def process_self_contained_coordinator_stream(
1857
1931
  # Convert list response to dict
1858
1932
  memory_stats = {}
1859
1933
  for i in range(0, len(memory_stats_raw), 2):
1860
- key = memory_stats_raw[i].decode() if isinstance(memory_stats_raw[i], bytes) else str(memory_stats_raw[i])
1934
+ key = (
1935
+ memory_stats_raw[i].decode()
1936
+ if isinstance(memory_stats_raw[i], bytes)
1937
+ else str(memory_stats_raw[i])
1938
+ )
1861
1939
  value = memory_stats_raw[i + 1]
1862
1940
  if isinstance(value, bytes):
1863
1941
  try:
@@ -1876,7 +1954,9 @@ def process_self_contained_coordinator_stream(
1876
1954
  "keys.bytes-per-key": 0,
1877
1955
  "dataset.percentage": 0,
1878
1956
  "overhead.total": 0,
1879
- "fragmentation": info.get("mem_fragmentation_ratio", 1.0),
1957
+ "fragmentation": info.get(
1958
+ "mem_fragmentation_ratio", 1.0
1959
+ ),
1880
1960
  "fragmentation.bytes": 0,
1881
1961
  "allocator.allocated": info.get("used_memory", 0),
1882
1962
  "allocator.resident": info.get("used_memory_rss", 0),
@@ -1884,9 +1964,13 @@ def process_self_contained_coordinator_stream(
1884
1964
  }
1885
1965
 
1886
1966
  # Detect object encoding by scanning 1% of the dataset
1887
- object_encoding_info = detect_object_encoding(r, benchmark_config.get("dbconfig", {}))
1888
- logging.info(f"Object encoding detection: {object_encoding_info.get('encoding', 'unknown')} "
1889
- f"({object_encoding_info.get('confidence', 0)*100:.1f}% confidence)")
1967
+ object_encoding_info = detect_object_encoding(
1968
+ r, benchmark_config.get("dbconfig", {})
1969
+ )
1970
+ logging.info(
1971
+ f"Object encoding detection: {object_encoding_info.get('encoding', 'unknown')} "
1972
+ f"({object_encoding_info.get('confidence', 0)*100:.1f}% confidence)"
1973
+ )
1890
1974
 
1891
1975
  # Extract key memory metrics
1892
1976
  memory_result = {
@@ -1894,51 +1978,107 @@ def process_self_contained_coordinator_stream(
1894
1978
  "total_allocated": memory_stats.get("total.allocated", 0),
1895
1979
  "dataset_bytes": memory_stats.get("dataset.bytes", 0),
1896
1980
  "keys_count": memory_stats.get("keys.count", 0),
1897
- "keys_bytes_per_key": memory_stats.get("keys.bytes-per-key", 0),
1898
- "dataset_percentage": memory_stats.get("dataset.percentage", 0),
1981
+ "keys_bytes_per_key": memory_stats.get(
1982
+ "keys.bytes-per-key", 0
1983
+ ),
1984
+ "dataset_percentage": memory_stats.get(
1985
+ "dataset.percentage", 0
1986
+ ),
1899
1987
  "overhead_total": memory_stats.get("overhead.total", 0),
1900
1988
  "fragmentation": memory_stats.get("fragmentation", 0),
1901
- "fragmentation_bytes": memory_stats.get("fragmentation.bytes", 0),
1902
- "allocator_allocated": memory_stats.get("allocator.allocated", 0),
1903
- "allocator_resident": memory_stats.get("allocator.resident", 0),
1904
- "allocator_fragmentation_ratio": memory_stats.get("allocator-fragmentation.ratio", 0),
1989
+ "fragmentation_bytes": memory_stats.get(
1990
+ "fragmentation.bytes", 0
1991
+ ),
1992
+ "allocator_allocated": memory_stats.get(
1993
+ "allocator.allocated", 0
1994
+ ),
1995
+ "allocator_resident": memory_stats.get(
1996
+ "allocator.resident", 0
1997
+ ),
1998
+ "allocator_fragmentation_ratio": memory_stats.get(
1999
+ "allocator-fragmentation.ratio", 0
2000
+ ),
1905
2001
  # Object encoding information
1906
- "object_encoding": object_encoding_info.get("encoding", "unknown"),
1907
- "encoding_confidence": object_encoding_info.get("confidence", 0.0),
1908
- "encoding_sample_size": object_encoding_info.get("sample_size", 0),
1909
- "encoding_distribution": object_encoding_info.get("encoding_distribution", {}),
1910
- "encoding_is_complete_scan": object_encoding_info.get("is_complete_scan", False),
2002
+ "object_encoding": object_encoding_info.get(
2003
+ "encoding", "unknown"
2004
+ ),
2005
+ "encoding_confidence": object_encoding_info.get(
2006
+ "confidence", 0.0
2007
+ ),
2008
+ "encoding_sample_size": object_encoding_info.get(
2009
+ "sample_size", 0
2010
+ ),
2011
+ "encoding_distribution": object_encoding_info.get(
2012
+ "encoding_distribution", {}
2013
+ ),
2014
+ "encoding_is_complete_scan": object_encoding_info.get(
2015
+ "is_complete_scan", False
2016
+ ),
1911
2017
  }
1912
2018
  memory_results.append(memory_result)
1913
2019
 
1914
2020
  # Push memory metrics to datasink
1915
2021
  if datasink_push_results_redistimeseries:
1916
2022
  memory_metrics_dict = {
1917
- "memory.total_allocated": memory_result["total_allocated"],
2023
+ "memory.total_allocated": memory_result[
2024
+ "total_allocated"
2025
+ ],
1918
2026
  "memory.dataset_bytes": memory_result["dataset_bytes"],
1919
2027
  "memory.keys_count": memory_result["keys_count"],
1920
- "memory.keys_bytes_per_key": memory_result["keys_bytes_per_key"],
1921
- "memory.dataset_percentage": memory_result["dataset_percentage"],
1922
- "memory.overhead_total": memory_result["overhead_total"],
2028
+ "memory.keys_bytes_per_key": memory_result[
2029
+ "keys_bytes_per_key"
2030
+ ],
2031
+ "memory.dataset_percentage": memory_result[
2032
+ "dataset_percentage"
2033
+ ],
2034
+ "memory.overhead_total": memory_result[
2035
+ "overhead_total"
2036
+ ],
1923
2037
  "memory.fragmentation": memory_result["fragmentation"],
1924
- "memory.fragmentation_bytes": memory_result["fragmentation_bytes"],
1925
- "memory.allocator_allocated": memory_result["allocator_allocated"],
1926
- "memory.allocator_resident": memory_result["allocator_resident"],
1927
- "memory.allocator_fragmentation_ratio": memory_result["allocator_fragmentation_ratio"],
1928
- "memory.encoding_confidence": memory_result["encoding_confidence"],
1929
- "memory.encoding_sample_size": memory_result["encoding_sample_size"],
2038
+ "memory.fragmentation_bytes": memory_result[
2039
+ "fragmentation_bytes"
2040
+ ],
2041
+ "memory.allocator_allocated": memory_result[
2042
+ "allocator_allocated"
2043
+ ],
2044
+ "memory.allocator_resident": memory_result[
2045
+ "allocator_resident"
2046
+ ],
2047
+ "memory.allocator_fragmentation_ratio": memory_result[
2048
+ "allocator_fragmentation_ratio"
2049
+ ],
2050
+ "memory.encoding_confidence": memory_result[
2051
+ "encoding_confidence"
2052
+ ],
2053
+ "memory.encoding_sample_size": memory_result[
2054
+ "encoding_sample_size"
2055
+ ],
1930
2056
  }
1931
2057
 
1932
2058
  # Add object encoding to metadata
1933
- metadata["object_encoding"] = memory_result["object_encoding"]
1934
- metadata["encoding_confidence"] = f"{memory_result['encoding_confidence']:.3f}"
1935
- metadata["encoding_sample_size"] = str(memory_result["encoding_sample_size"])
1936
- metadata["encoding_scan_type"] = "complete" if memory_result.get("encoding_is_complete_scan", False) else "sample"
2059
+ metadata["object_encoding"] = memory_result[
2060
+ "object_encoding"
2061
+ ]
2062
+ metadata["encoding_confidence"] = (
2063
+ f"{memory_result['encoding_confidence']:.3f}"
2064
+ )
2065
+ metadata["encoding_sample_size"] = str(
2066
+ memory_result["encoding_sample_size"]
2067
+ )
2068
+ metadata["encoding_scan_type"] = (
2069
+ "complete"
2070
+ if memory_result.get("encoding_is_complete_scan", False)
2071
+ else "sample"
2072
+ )
1937
2073
 
1938
2074
  # Add encoding distribution to metadata if multiple encodings found
1939
2075
  if len(memory_result["encoding_distribution"]) > 1:
1940
- for enc, percentage in memory_result["encoding_distribution"].items():
1941
- metadata[f"encoding_dist_{enc}"] = f"{percentage:.1f}%"
2076
+ for enc, percentage in memory_result[
2077
+ "encoding_distribution"
2078
+ ].items():
2079
+ metadata[f"encoding_dist_{enc}"] = (
2080
+ f"{percentage:.1f}%"
2081
+ )
1942
2082
 
1943
2083
  # Set datapoint_time_ms for memory comparison mode
1944
2084
  datapoint_time_ms = start_time_ms
@@ -1947,9 +2087,15 @@ def process_self_contained_coordinator_stream(
1947
2087
  metadata["metric-type"] = "memory-stats"
1948
2088
 
1949
2089
  # Debug: Check git_hash value and memory metrics before export
1950
- logging.info(f"DEBUG: About to export memory metrics with git_hash='{git_hash}', type={type(git_hash)}")
1951
- logging.info(f"DEBUG: memory_metrics_dict has {len(memory_metrics_dict)} items: {list(memory_metrics_dict.keys())}")
1952
- logging.info(f"DEBUG: Sample values: {dict(list(memory_metrics_dict.items())[:3])}")
2090
+ logging.info(
2091
+ f"DEBUG: About to export memory metrics with git_hash='{git_hash}', type={type(git_hash)}"
2092
+ )
2093
+ logging.info(
2094
+ f"DEBUG: memory_metrics_dict has {len(memory_metrics_dict)} items: {list(memory_metrics_dict.keys())}"
2095
+ )
2096
+ logging.info(
2097
+ f"DEBUG: Sample values: {dict(list(memory_metrics_dict.items())[:3])}"
2098
+ )
1953
2099
  export_redis_metrics(
1954
2100
  git_version,
1955
2101
  datapoint_time_ms,
@@ -1967,7 +2113,7 @@ def process_self_contained_coordinator_stream(
1967
2113
  git_hash,
1968
2114
  running_platform,
1969
2115
  )
1970
-
2116
+
1971
2117
  exporter_datasink_common(
1972
2118
  benchmark_config,
1973
2119
  0, # benchmark_duration_seconds = 0 for memory only
@@ -1998,13 +2144,19 @@ def process_self_contained_coordinator_stream(
1998
2144
  # Send MEMORY PURGE after memory comparison (if FLUSHALL at test end is not enabled)
1999
2145
  if not args.flushall_on_every_test_end:
2000
2146
  try:
2001
- logging.info("Sending MEMORY PURGE after memory comparison")
2147
+ logging.info(
2148
+ "Sending MEMORY PURGE after memory comparison"
2149
+ )
2002
2150
  for conn in redis_conns:
2003
2151
  conn.execute_command("MEMORY", "PURGE")
2004
2152
  except Exception as e:
2005
- logging.warning(f"MEMORY PURGE failed after memory comparison: {e}")
2153
+ logging.warning(
2154
+ f"MEMORY PURGE failed after memory comparison: {e}"
2155
+ )
2006
2156
 
2007
- logging.info(f"Memory comparison completed for test {test_name}")
2157
+ logging.info(
2158
+ f"Memory comparison completed for test {test_name}"
2159
+ )
2008
2160
  delete_temporary_files(
2009
2161
  temporary_dir_client=temporary_dir_client,
2010
2162
  full_result_path=None,
@@ -2579,11 +2731,15 @@ def process_self_contained_coordinator_stream(
2579
2731
  # Send MEMORY PURGE after FLUSHALL for memory comparison mode
2580
2732
  if memory_comparison_only:
2581
2733
  try:
2582
- logging.info("Sending MEMORY PURGE after FLUSHALL at test end")
2734
+ logging.info(
2735
+ "Sending MEMORY PURGE after FLUSHALL at test end"
2736
+ )
2583
2737
  for r in redis_conns:
2584
2738
  r.execute_command("MEMORY", "PURGE")
2585
2739
  except Exception as e:
2586
- logging.warning(f"MEMORY PURGE failed after FLUSHALL at test end: {e}")
2740
+ logging.warning(
2741
+ f"MEMORY PURGE failed after FLUSHALL at test end: {e}"
2742
+ )
2587
2743
 
2588
2744
  except KeyboardInterrupt:
2589
2745
  logging.info("KeyboardInterrupt caught. Exiting...")
@@ -2602,7 +2758,9 @@ def process_self_contained_coordinator_stream(
2602
2758
 
2603
2759
  # Check if user requested exit via Ctrl+C
2604
2760
  if _exit_requested:
2605
- logging.info("Exit requested by user. Stopping after exception.")
2761
+ logging.info(
2762
+ "Exit requested by user. Stopping after exception."
2763
+ )
2606
2764
  break
2607
2765
  # tear-down
2608
2766
  logging.info("Tearing down setup")
@@ -2641,7 +2799,9 @@ def process_self_contained_coordinator_stream(
2641
2799
  # Check if user requested exit via Ctrl+C
2642
2800
  if _exit_requested:
2643
2801
  logging.info("Exit requested by user. Printing summary before exit.")
2644
- print("\nExecution stopped by user request. Printing summary of completed tests...")
2802
+ print(
2803
+ "\nExecution stopped by user request. Printing summary of completed tests..."
2804
+ )
2645
2805
 
2646
2806
  # Print Redis server information section before results
2647
2807
  if len(results_matrix) > 0:
@@ -2667,7 +2827,9 @@ def process_self_contained_coordinator_stream(
2667
2827
 
2668
2828
  # Add note if execution was stopped early
2669
2829
  if _exit_requested:
2670
- print("\n(Note: Execution was stopped early by user request - showing results for completed tests only)")
2830
+ print(
2831
+ "\n(Note: Execution was stopped early by user request - showing results for completed tests only)"
2832
+ )
2671
2833
 
2672
2834
  if client_aggregated_results_folder != "":
2673
2835
  os.makedirs(client_aggregated_results_folder, exist_ok=True)
@@ -2683,13 +2845,13 @@ def process_self_contained_coordinator_stream(
2683
2845
 
2684
2846
  # Print memory comparison summary if in memory comparison mode
2685
2847
  if memory_comparison_only and memory_results:
2686
- logging.info("\n" + "="*80)
2848
+ logging.info("\n" + "=" * 80)
2687
2849
  logging.info("MEMORY COMPARISON SUMMARY")
2688
- logging.info("="*80)
2850
+ logging.info("=" * 80)
2689
2851
  logging.info(f"Total unique datasets loaded: {len(loaded_datasets)}")
2690
2852
  if loaded_datasets:
2691
2853
  logging.info(f"Datasets: {', '.join(sorted(loaded_datasets))}")
2692
- logging.info("="*80)
2854
+ logging.info("=" * 80)
2693
2855
 
2694
2856
  # Create memory summary table
2695
2857
  memory_headers = [
@@ -2704,7 +2866,7 @@ def process_self_contained_coordinator_stream(
2704
2866
  "Alloc Fragmentation",
2705
2867
  "Object Encoding",
2706
2868
  "Encoding Confidence",
2707
- "Scan Type"
2869
+ "Scan Type",
2708
2870
  ]
2709
2871
 
2710
2872
  memory_matrix = []
@@ -2714,20 +2876,26 @@ def process_self_contained_coordinator_stream(
2714
2876
  dataset_mb = result["dataset_bytes"] / (1024 * 1024)
2715
2877
  overhead_mb = result["overhead_total"] / (1024 * 1024)
2716
2878
 
2717
- memory_matrix.append([
2718
- result["test_name"],
2719
- f"{total_mb:.1f}MB",
2720
- f"{dataset_mb:.1f}MB",
2721
- f"{result['keys_count']:,}",
2722
- f"{result['keys_bytes_per_key']:.0f}B",
2723
- f"{result['dataset_percentage']:.1f}%",
2724
- f"{overhead_mb:.1f}MB",
2725
- f"{result['fragmentation']:.2f}",
2726
- f"{result['allocator_fragmentation_ratio']:.3f}",
2727
- result.get("object_encoding", "unknown"),
2728
- f"{result.get('encoding_confidence', 0.0)*100:.1f}%",
2729
- "complete" if result.get("encoding_is_complete_scan", False) else "sample"
2730
- ])
2879
+ memory_matrix.append(
2880
+ [
2881
+ result["test_name"],
2882
+ f"{total_mb:.1f}MB",
2883
+ f"{dataset_mb:.1f}MB",
2884
+ f"{result['keys_count']:,}",
2885
+ f"{result['keys_bytes_per_key']:.0f}B",
2886
+ f"{result['dataset_percentage']:.1f}%",
2887
+ f"{overhead_mb:.1f}MB",
2888
+ f"{result['fragmentation']:.2f}",
2889
+ f"{result['allocator_fragmentation_ratio']:.3f}",
2890
+ result.get("object_encoding", "unknown"),
2891
+ f"{result.get('encoding_confidence', 0.0)*100:.1f}%",
2892
+ (
2893
+ "complete"
2894
+ if result.get("encoding_is_complete_scan", False)
2895
+ else "sample"
2896
+ ),
2897
+ ]
2898
+ )
2731
2899
 
2732
2900
  memory_writer = MarkdownTableWriter(
2733
2901
  table_name="Memory Usage Summary",
@@ -2737,9 +2905,13 @@ def process_self_contained_coordinator_stream(
2737
2905
  memory_writer.write_table()
2738
2906
 
2739
2907
  if dry_run is True:
2740
- mode_description = "memory comparison" if memory_comparison_only else "benchmark"
2908
+ mode_description = (
2909
+ "memory comparison" if memory_comparison_only else "benchmark"
2910
+ )
2741
2911
  logging.info(
2742
- "Number of tests that would have been run ({}): {}".format(mode_description, dry_run_count)
2912
+ "Number of tests that would have been run ({}): {}".format(
2913
+ mode_description, dry_run_count
2914
+ )
2743
2915
  )
2744
2916
  if _exit_requested:
2745
2917
  logging.info("(Note: Execution was stopped early by user request)")
@@ -2762,14 +2934,25 @@ def process_self_contained_coordinator_stream(
2762
2934
  with open(test_file, "r") as stream:
2763
2935
  benchmark_config = yaml.safe_load(stream)
2764
2936
 
2765
- test_name = extract_test_name_from_test_configuration_file(test_file)
2766
- if test_name in dry_run_tests and "dbconfig" in benchmark_config:
2937
+ test_name = extract_test_name_from_test_configuration_file(
2938
+ test_file
2939
+ )
2940
+ if (
2941
+ test_name in dry_run_tests
2942
+ and "dbconfig" in benchmark_config
2943
+ ):
2767
2944
  # Skip load tests in dry run analysis too
2768
- keyspacelen = benchmark_config["dbconfig"].get("check", {}).get("keyspacelen", None)
2945
+ keyspacelen = (
2946
+ benchmark_config["dbconfig"]
2947
+ .get("check", {})
2948
+ .get("keyspacelen", None)
2949
+ )
2769
2950
  if keyspacelen is not None and keyspacelen == 0:
2770
2951
  continue
2771
2952
 
2772
- dataset_name = benchmark_config["dbconfig"].get("dataset_name")
2953
+ dataset_name = benchmark_config["dbconfig"].get(
2954
+ "dataset_name"
2955
+ )
2773
2956
  if dataset_name:
2774
2957
  unique_datasets.add(dataset_name)
2775
2958
  tests_with_datasets += 1
@@ -2781,7 +2964,9 @@ def process_self_contained_coordinator_stream(
2781
2964
  logging.info(f"\nMemory comparison analysis:")
2782
2965
  logging.info(f" Tests with datasets: {tests_with_datasets}")
2783
2966
  logging.info(f" Unique datasets: {len(unique_datasets)}")
2784
- logging.info(f" Dataset ingestion savings: {tests_with_datasets - len(unique_datasets)} skipped loads")
2967
+ logging.info(
2968
+ f" Dataset ingestion savings: {tests_with_datasets - len(unique_datasets)} skipped loads"
2969
+ )
2785
2970
  if unique_datasets:
2786
2971
  logging.info(f" Datasets that would be loaded:")
2787
2972
  for dataset in sorted(unique_datasets):
@@ -3016,7 +3201,9 @@ def get_supported_redis_commands(redis_conns):
3016
3201
 
3017
3202
  # Handle case where COMMAND returns 0 commands (likely not supported)
3018
3203
  if len(supported_commands) == 0:
3019
- logging.warning("COMMAND returned 0 commands - likely not supported by this Redis instance")
3204
+ logging.warning(
3205
+ "COMMAND returned 0 commands - likely not supported by this Redis instance"
3206
+ )
3020
3207
  return None
3021
3208
 
3022
3209
  # Log some sample commands for debugging
@@ -3042,7 +3229,9 @@ def get_supported_redis_commands(redis_conns):
3042
3229
  def check_test_command_support(benchmark_config, supported_commands):
3043
3230
  """Check if all tested-commands in the benchmark config are supported"""
3044
3231
  if supported_commands is None or len(supported_commands) == 0:
3045
- logging.warning("No supported commands list available (COMMAND not supported or returned 0 commands), skipping command check")
3232
+ logging.warning(
3233
+ "No supported commands list available (COMMAND not supported or returned 0 commands), skipping command check"
3234
+ )
3046
3235
  return True, []
3047
3236
 
3048
3237
  if "tested-commands" not in benchmark_config: