Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-09 10:47:24

0001 #!/usr/bin/env python3
0002 """request-sizes-build.py — build the record the Request size plot page
0003 renders: how large the ePIC production requests are.
0004 
0005 Every request record that states a number of events is read (a form
0006 answer in the requester's own words, or a recorded count), the
0007 distribution computed, and both stored as the cached product
0008 ``request_sizes:v1`` (swf-epicprod ``swf_epicprod/request_events.py``,
0009 docs/REQUEST_SIZES.md). The page reads that row and never builds, so a
0010 page render costs a row read.
0011 
0012 The prod-ops agent's doer for ``request_sizes_build``, nightly. Requests
0013 arrive by the day, so nothing is lost by building once a night.
0014 Django-bootstrap standalone script, also usable by hand::
0015 
0016     cd /data/wenauseic/github/swf-monitor/src
0017     source ../../swf-testbed/.venv/bin/activate && source ~/.env
0018     python ../scripts/request-sizes-build.py [--dry-run]
0019 
0020 ``--dry-run`` reads and prints, and stores nothing.
0021 Prints one ``SUMMARY`` JSON line; exit 1 on failure.
0022 """
0023 import argparse
0024 import json
0025 import logging
0026 import os
0027 import sys
0028 
0029 THIS_DIR = os.path.dirname(os.path.abspath(__file__))
0030 sys.path.insert(0, os.path.join(THIS_DIR, '..', 'src'))
0031 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'swf_monitor_project.settings')
0032 
0033 import django  # noqa: E402
0034 django.setup()
0035 
0036 KEY = 'request_sizes:v1'
0037 TTL_S = 7 * 24 * 3600      # the page shows what was built; staleness is visible
0038 
0039 
0040 def main():
0041     ap = argparse.ArgumentParser(description=__doc__.split('\n\n')[0])
0042     ap.add_argument('--dry-run', action='store_true',
0043                     help='read and print, store nothing')
0044     ap.add_argument('--created-by', default='request-sizes',
0045                     help='who ran the build (the record carries it)')
0046     args = ap.parse_args()
0047 
0048     from django.utils import timezone
0049     from monitor_app.cached_product import get_product
0050     from monitor_app.epicprod_logging import log_epicprod_action
0051     from swf_epicprod.request_events import build, human
0052 
0053     started = timezone.now()
0054     try:
0055         state = build()
0056     except Exception as exc:  # noqa: BLE001
0057         print(f'ERROR: {type(exc).__name__}: {exc}', file=sys.stderr)
0058         return 1
0059     summary = state['summary']
0060     duration_s = (timezone.now() - started).total_seconds()
0061 
0062     if not args.dry_run:
0063         try:
0064             get_product(KEY, lambda: state, ttl_seconds=TTL_S, refresh=True)
0065         except Exception as exc:  # noqa: BLE001
0066             print(f'ERROR: storing {KEY}: {type(exc).__name__}: {exc}', file=sys.stderr)
0067             return 1
0068         log_epicprod_action(
0069             'request-sizes', 'request_sizes_build', username=args.created_by,
0070             outcome='ok', duration_ms=int(duration_s * 1000),
0071             sublevel='low', live_default=False, level=logging.INFO,
0072             message=(f'request sizes built: {summary["stated"]} of '
0073                      f'{summary["requests"]} requests state a count, median '
0074                      f'{human(summary["percentiles"].get("50"))}, '
0075                      f'p90 {human(summary["percentiles"].get("90"))}'),
0076             requests=summary['requests'], stated=summary['stated'],
0077             unstated=summary['unstated'], total_events=summary['total'])
0078 
0079     print(f"requests {summary['requests']}, stating a count {summary['stated']}, "
0080           f"median {human(summary['percentiles'].get('50'))}, "
0081           f"p90 {human(summary['percentiles'].get('90'))}, "
0082           f"max {human(summary['max'])}")
0083     print('SUMMARY ' + json.dumps(
0084         {k: v for k, v in summary.items()
0085          if k not in ('bins', 'cumulative', 'thresholds', 'apart')},
0086         default=str))
0087     return 0
0088 
0089 
0090 if __name__ == '__main__':
0091     sys.exit(main())