annotate lib/background_loop.py @ 2218:436a1fdbfe4a

comment
author drewp@bigasterisk.com
date Tue, 23 May 2023 15:52:26 -0700
parents acf1b68d031a
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
1 # dev copy
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
2 import asyncio
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
3 import logging
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
4 import time
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
5 import traceback
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
6 from typing import Any, Awaitable, Callable, Union
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
7
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
8 from prometheus_client import Gauge, Summary
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
9 from light9.recentfps import RecentFps
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
10 from braillegraph import horizontal_graph
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
11
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
12 log = logging.getLogger('loop')
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
13
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
14
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
15 # throw this away (when net_routes is rewritten)
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
16 def loop_forever_sync(func, sleep_period, up_metric):
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
17 first_run = True
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
18 while True:
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
19 try:
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
20 func(first_run)
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
21 up_metric.set(1)
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
22 first_run = False
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
23 except Exception as ex:
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
24 log.error(ex)
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
25 traceback.print_exc()
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
26 up_metric.set(0)
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
27 time.sleep(sleep_period)
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
28
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
29
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
30 _created = []
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
31
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
32 # todo: some tricky typing
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
33 F_RET = Any
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
34 F_KWARGS = Any
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
35
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
36 UserAsyncFunc = Callable[..., # always called with at least kwarg first_run=bool
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
37 Awaitable[F_RET]]
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
38 UserSyncFunc = Callable[..., # see above
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
39 F_RET]
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
40 UserFunc = Union[UserAsyncFunc, UserSyncFunc]
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
41
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
42
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
43 class Loop:
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
44
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
45 def __init__(
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
46 self,
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
47 func: UserFunc,
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
48 sleep_period: float,
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
49 metric_prefix: str,
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
50 extra_sleep_on_error: float = 2,
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
51 log_fps=False,
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
52 ):
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
53 self.func = func
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
54 self.sleep_period = sleep_period
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
55 self.extra_sleep_on_error = extra_sleep_on_error
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
56 self.metric_prefix = metric_prefix
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
57
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
58 self.up_metric = Gauge(f'{metric_prefix}_up', 'not erroring')
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
59 self.call_metric = Summary(f'{metric_prefix}_calls', 'calls')
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
60 self.lastSuccessRun = 0
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
61 self.everSucceeded = False
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
62 self.succeeding = False
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
63 self.fps = RecentFps() if log_fps else None
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
64 self.lastFpsLog = 0
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
65
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
66 async def call_func(self, first_run: bool, call_kwargs={}) -> F_RET:
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
67 with self.call_metric.time():
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
68 if asyncio.iscoroutinefunction(self.func):
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
69 ret = await self.func(first_run=first_run, **call_kwargs)
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
70 else:
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
71 ret = self.func(first_run=first_run, **call_kwargs)
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
72 return ret
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
73
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
74 async def _run(self):
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
75 self.first_run = True
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
76 self.newlyFailing = True
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
77 while True:
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
78 await self._runOne()
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
79 await asyncio.sleep(self.sleep_period)
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
80
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
81 async def runNow(self, **kwargs: F_KWARGS) -> F_RET:
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
82 """unlike background runs, you get to pass more args and get your func's
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
83 return value.
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
84 """
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
85 return await self._runOne(call_kwargs=kwargs)
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
86
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
87 async def _runOne(self, call_kwargs={}):
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
88 now = time.time()
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
89 self._updateFps(now)
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
90 try:
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
91 result = await self.call_func(self.first_run, call_kwargs)
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
92 self.lastSuccessRun = time.time()
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
93 if self.fps:
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
94 self.fps.mark()
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
95 self.up_metric.set(1)
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
96 self.everSucceeded = True
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
97 self.newlyFailing = True
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
98 self.first_run = False
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
99 except Exception as ex:
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
100 if self.newlyFailing:
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
101 traceback.print_exc()
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
102 self.newlyFailing = False
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
103 else:
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
104 log.error(ex)
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
105 self.up_metric.set(0)
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
106 result = None
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
107 self.succeeding = False
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
108
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
109 await asyncio.sleep(self.extra_sleep_on_error)
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
110 # todo: something that reveals error ratio
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
111 return result
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
112
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
113 def _updateFps(self, now: float):
2218
436a1fdbfe4a comment
drewp@bigasterisk.com
parents: 2216
diff changeset
114 # not sure i even want this- it's redundant with some metrics code
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
115 if self.fps is None:
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
116 return
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
117 if now < self.lastFpsLog + 5:
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
118 return
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
119 d = self.fps()
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
120 y_hi = 1 / self.sleep_period
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
121 if not d:
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
122 return
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
123 pts = [int(min(4, y / y_hi * 4)) for y in d['recents']]
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
124 log.info(f'{self.metric_prefix} fps={d["average"]:3.1f} (req={y_hi:3.1f}) {horizontal_graph(pts)}')
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
125 self.lastFpsLog = now
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
126
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
127
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
128 def loop_forever(
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
129 func: UserFunc,
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
130 sleep_period: float,
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
131 metric_prefix='background_loop',
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
132 up_metric=None,
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
133 call_metric=None,
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
134 extra_sleep_on_error=2,
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
135 log_fps=False,
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
136 ):
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
137 """
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
138 sleep_period is the sleep time after however long func takes to run
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
139 """
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
140 if up_metric is not None or call_metric is not None:
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
141 raise NotImplementedError('remove old-style metrics')
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
142
2216
acf1b68d031a fancier background_loop reporting for faders
drewp@bigasterisk.com
parents: 2215
diff changeset
143 loop = Loop(func, sleep_period, metric_prefix, extra_sleep_on_error, log_fps)
2215
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
144 _created.append(asyncio.create_task(loop._run()))
d8853f173568 fork background_loop
drewp@bigasterisk.com
parents:
diff changeset
145 return loop