"""Optional synthetic read-projection benchmark; never pass a production DB. python tests/benchmark_read_history.py --jobs 20000 --out /tmp/aim-history-benchmark.json Uses an automatically cleaned temporary database and only retained WebGUI fixtures. """ from pathlib import Path import argparse import json import resource import statistics import sys import tempfile import time sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'src')) from aim_webgui.activity import Activity, HistoryFilter from aim_webgui.auth.service import Auth from aim_webgui.config import Settings def main(): parser=argparse.ArgumentParser() parser.add_argument('--jobs',type=int,default=20000) parser.add_argument('--out',type=Path,required=True) args=parser.parse_args() if not 1 <= args.jobs <= 100000:parser.error('--jobs must be 1..100000') counts=('ok','changed','failures','unreachable','skipped','rescued','ignored') now=int(time.time()) with tempfile.TemporaryDirectory(prefix='aim-read-benchmark-') as tmp: auth=Auth(Settings(state_dir=Path(tmp)/'state',public_url='https://fixture.invalid').validate()) auth.bootstrap() with auth.store.transaction() as db: db.execute('UPDATE users SET must_change_password=0') actor=db.execute('SELECT id FROM users WHERE username=?',('admin',)).fetchone()[0] for i in range(args.jobs): hosts=[f'host-{(i+j)%200:04d}.example' for j in range(4)] outcome='unreachable' if i%5==0 else 'successful' targets=[] for j,h in enumerate(hosts): c=dict.fromkeys(counts,0);c['ok']=30;c['changed']=1 state=outcome if j==3 else 'successful' if state=='unreachable':c['unreachable']=1 targets.append({'host':h,'outcome':state,'counts':c}) unreachable=int(outcome=='unreachable') result={'status':'failed' if unreachable else 'succeeded','targets':targets, 'target_summary':{'schema':'target_outcome_summary_v1','requested':4,'accounted':4,'complete':True, 'successful':4-unreachable,'failed':0,'unreachable':unreachable,'indeterminate':0,'not_started':0}} plan={'customer':'synthetic','playbook':f'debug_fixture_{i%8}','targets':hosts} db.execute('INSERT INTO jobs(id,owner_id,plan,mode,status,created_at,scheduled_at,finished_at,core_result) VALUES(?,?,?,?,?,?,?,?,?)', (f'{i:032x}',actor,json.dumps(plan),'apply','failed' if unreachable else 'successful',now-i-1,now-i-1,now-i-1,json.dumps(result))) reader=Activity(auth.settings);durations=[] for _ in range(3): start=time.perf_counter();report=reader.report(actor,HistoryFilter(days='all'),now=now) durations.append(time.perf_counter()-start) assert report['jobs']==args.jobs and report['samples']==args.jobs*4 assert len(report['records'])==min(25,args.jobs*4) and len(report['matrix'])<=20 result={'synthetic_only':True,'jobs':args.jobs,'host_run_samples':report['samples'], 'distinct_customer_hosts':report['host_count'],'playbooks':len(report['playbooks']), 'runs_seconds':durations,'median_seconds':statistics.median(durations), 'process_peak_rss_kib':resource.getrusage(resource.RUSAGE_SELF).ru_maxrss, 'sqlite_bytes':auth.settings.database.stat().st_size,'output_records':len(report['records']), 'matrix_rows':len(report['matrix']),'core_calls':0,'projection_tables_added':0} args.out.write_text(json.dumps(result,indent=2)+'\n') print(json.dumps(result,indent=2)) if __name__=='__main__':main()