v0.34.0_1
This commit is contained in:
1 parent
9b3d6731cc
commit
00f29f92c8
68 files changed
+2599
-513
No files matched your search
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Deterministic analytical/synthetic cache-retention scenario, not a RustProxy load test.
|
||||
|
||||
Model: 20 vhosts with 100 HOT + 100 COLD uniformly requested objects each.
|
||||
A new configuration is saved 1,000 times. Eighty percent of saves edit only
|
||||
keepalive/LB operational controls, and 20% edit content policy on one vhost.
|
||||
Every save is followed by 500 uniform GETs; every object is public/cacheable and
|
||||
has infinite TTL. A miss immediately inserts the object in its original tier.
|
||||
Capacity is assumed adequate and there is no origin latency or concurrent IO.
|
||||
The baseline flushes both tiers globally on every save, as in 0.33.8.
|
||||
The candidate changes only the affected vhost after content edits.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import random
|
||||
|
||||
|
||||
def simulate(vhosts=20, objects_per_tier=100, saves=1000, requests_per_save=500, seed=3400):
|
||||
if min(vhosts, objects_per_tier, saves, requests_per_save) < 1:
|
||||
raise ValueError("all counts must be positive")
|
||||
rng = random.Random(seed)
|
||||
objects = vhosts * objects_per_tier * 2
|
||||
old = set(range(objects))
|
||||
new = set(range(objects))
|
||||
old_hits = new_hits = old_misses = new_misses = 0
|
||||
old_evicted = new_evicted = 0
|
||||
edits_operational = edits_content = 0
|
||||
for step in range(saves):
|
||||
changed_vhost = rng.randrange(vhosts)
|
||||
content_edit = step % 5 == 0
|
||||
if content_edit:
|
||||
edits_content += 1
|
||||
else:
|
||||
edits_operational += 1
|
||||
old_evicted += len(old)
|
||||
old.clear()
|
||||
if content_edit:
|
||||
low = changed_vhost * objects_per_tier * 2
|
||||
high = low + objects_per_tier * 2
|
||||
removed = set(range(low, high)) & new
|
||||
new_evicted += len(removed)
|
||||
new.difference_update(removed)
|
||||
for _ in range(requests_per_save):
|
||||
object_id = rng.randrange(objects)
|
||||
if object_id in old:
|
||||
old_hits += 1
|
||||
else:
|
||||
old_misses += 1
|
||||
old.add(object_id)
|
||||
if object_id in new:
|
||||
new_hits += 1
|
||||
else:
|
||||
new_misses += 1
|
||||
new.add(object_id)
|
||||
total = saves * requests_per_save
|
||||
assert old_hits + old_misses == total
|
||||
assert new_hits + new_misses == total
|
||||
assert new_evicted <= old_evicted
|
||||
assert new_misses <= old_misses
|
||||
return {
|
||||
"scenario": {
|
||||
"vhosts": vhosts,
|
||||
"hot_objects_per_vhost": objects_per_tier,
|
||||
"cold_objects_per_vhost": objects_per_tier,
|
||||
"saves": saves,
|
||||
"operational_saves": edits_operational,
|
||||
"single_vhost_content_saves": edits_content,
|
||||
"gets_per_save": requests_per_save,
|
||||
"total_gets": total,
|
||||
"seed": seed,
|
||||
"assumptions": "All objects public/cacheable, infinite TTL, instantly refilled, uniform GETs; no bandwidth/CPU/origin timings",
|
||||
},
|
||||
"legacy_global_purge": {
|
||||
"invalidated_objects": old_evicted,
|
||||
"misses": old_misses,
|
||||
"hits": old_hits,
|
||||
"hit_ratio_percent": round(100 * old_hits / total, 3),
|
||||
},
|
||||
"scoped_cache": {
|
||||
"invalidated_objects": new_evicted,
|
||||
"misses": new_misses,
|
||||
"hits": new_hits,
|
||||
"hit_ratio_percent": round(100 * new_hits / total, 3),
|
||||
},
|
||||
"improvement": {
|
||||
"fewer_invalidations_percent": round(100 * (1 - new_evicted / old_evicted), 3) if old_evicted else 0,
|
||||
"fewer_cache_misses_percent": round(100 * (1 - new_misses / old_misses), 3) if old_misses else 0,
|
||||
"hit_ratio_gain_percentage_points": round(100 * (new_hits - old_hits) / total, 3),
|
||||
"observed_rps_gain_percent": None,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--vhosts", type=int, default=20)
|
||||
parser.add_argument("--objects-per-tier", type=int, default=100)
|
||||
parser.add_argument("--saves", type=int, default=1000)
|
||||
parser.add_argument("--gets-per-save", type=int, default=500)
|
||||
parser.add_argument("--seed", type=int, default=3400)
|
||||
parser.add_argument("--output", help="optional JSON results file")
|
||||
args = parser.parse_args()
|
||||
result = simulate(args.vhosts, args.objects_per_tier, args.saves, args.gets_per_save, args.seed)
|
||||
encoded = json.dumps(result, indent=2, sort_keys=True)
|
||||
if args.output:
|
||||
from pathlib import Path
|
||||
Path(args.output).write_text(encoded + "\n")
|
||||
print(encoded)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user