python.d: use real time for calc sinceLast (#4720)
* use real time clock to calc since last update * python.d: remove unused attr from RuntimeCounters
Ilya Mashchenko committed
Nov 26, 2018 at 12:43 UTC
44eef8d1039dfea033a1caeb3e7fcfcc5d34a828
3 files changed
+54
-59
collectors/python.d.plugin/python_modules/bases/FrameworkServices/SimpleService.py
+52
-57
@@ -5,7 +5,7 @@
5
# SPDX-License-Identifier: GPL-3.0-or-later
6
7
from threading import Thread
8
-from time import sleep
8
+from time import sleep, time
9
10
from third_party.monotonic import monotonic
11
@@ -17,25 +17,42 @@ RUNTIME_CHART_UPDATE = 'BEGIN netdata.runtime_{job_name} {since_last}\n' \
17
'SET run_time = {elapsed}\n' \
18
'END\n'
19
20
+PENALTY_EVERY = 5
21
+
22
23
class RuntimeCounters:
24
def __init__(self, configuration):
25
"""
26
:param configuration: <dict>
27
"""
26
- self.FREQ = int(configuration.pop('update_every'))
27
- self.START_RUN = 0
28
- self.NEXT_RUN = 0
29
- self.PREV_UPDATE = 0
30
- self.SINCE_UPDATE = 0
31
- self.ELAPSED = 0
32
- self.RETRIES = 0
33
- self.RETRIES_MAX = configuration.pop('retries')
34
- self.PENALTY = 0
35
- self.RUNS = 1
36
-
37
- def is_sleep_time(self):
38
- return self.START_RUN < self.NEXT_RUN
28
+ self.update_every = int(configuration.pop('update_every'))
29
+ self.max_retries = int(configuration.pop('retries'))
30
+
31
+ self.start_mono = 0
32
+ self.start_real = 0
33
+ self.retries = 0
34
+ self.penalty = 0
35
+ self.elapsed = 0
36
+ self.prev_update = 0
37
+ self.runs = 1
38
+
39
+ def calc_next(self):
40
+ self.start_mono = monotonic()
41
+ return self.start_mono - (self.start_mono % self.update_every) + self.update_every + self.penalty
42
+
43
+ def sleep_until_next(self):
44
+ next_time = self.calc_next()
45
+ while self.start_mono < next_time:
46
+ sleep(next_time - self.start_mono)
47
+ self.start_mono = monotonic()
48
+ self.start_real = time()
49
+
50
+ def handle_retries(self):
51
+ self.retries += 1
52
+ if self.retries % PENALTY_EVERY:
53
+ return True
54
+ self.penalty = self.retries * self.update_every / 2
55
+ return self.retries < self.max_retries
56
57
58
class SimpleService(Thread, PythonDLimitedLogger, OldVersionCompatibility, object):
@@ -83,11 +100,11 @@ class SimpleService(Thread, PythonDLimitedLogger, OldVersionCompatibility, objec
100
101
@property
102
def runs_counter(self):
86
- return self._runtime_counters.RUNS
103
+ return self._runtime_counters.runs
104
105
@property
106
def update_every(self):
90
- return self._runtime_counters.FREQ
107
+ return self._runtime_counters.update_every
108
109
@update_every.setter
110
def update_every(self, value):
@@ -95,7 +112,7 @@ class SimpleService(Thread, PythonDLimitedLogger, OldVersionCompatibility, objec
112
:param value: <int>
113
:return:
114
"""
98
- self._runtime_counters.FREQ = value
115
+ self._runtime_counters.update_every = value
116
117
def get_update_every(self):
118
return self.update_every
@@ -163,41 +180,40 @@ class SimpleService(Thread, PythonDLimitedLogger, OldVersionCompatibility, objec
180
:return: None
181
"""
182
job = self._runtime_counters
166
- self.debug('started, update frequency: {freq}, '
167
- 'retries: {retries}'.format(freq=job.FREQ, retries=job.RETRIES_MAX - job.RETRIES))
183
+ self.debug('started, update frequency: {freq}, retries: {retries}'.format(
184
+ freq=job.update_every,
185
+ retries=job.max_retries - job.retries),
186
+ )
187
188
while True:
170
- job.START_RUN = monotonic()
171
-
172
- job.NEXT_RUN = job.START_RUN - (job.START_RUN % job.FREQ) + job.FREQ + job.PENALTY
173
-
174
- self.sleep_until_next_run()
189
+ job.sleep_until_next()
190
176
- if job.PREV_UPDATE:
177
- job.SINCE_UPDATE = int((job.START_RUN - job.PREV_UPDATE) * 1e6)
191
+ since = 0
192
+ if job.prev_update:
193
+ since = int((job.start_real - job.prev_update) * 1e6)
194
195
try:
180
- updated = self.update(interval=job.SINCE_UPDATE)
196
+ updated = self.update(interval=since)
197
except Exception as error:
198
self.error('update() unhandled exception: {error}'.format(error=error))
199
updated = False
200
185
- job.RUNS += 1
201
+ job.runs += 1
202
203
if not updated:
188
- if not self.manage_retries():
204
+ if not job.handle_retries():
205
return
206
else:
191
- job.ELAPSED = int((monotonic() - job.START_RUN) * 1e3)
192
- job.PREV_UPDATE = job.START_RUN
193
- job.RETRIES, job.PENALTY = 0, 0
207
+ job.elapsed = int((monotonic() - job.start_mono) * 1e3)
208
+ job.prev_update = job.start_real
209
+ job.retries, job.penalty = 0, 0
210
safe_print(RUNTIME_CHART_UPDATE.format(job_name=self.name,
195
- since_last=job.SINCE_UPDATE,
196
- elapsed=job.ELAPSED))
211
+ since_last=since,
212
+ elapsed=job.elapsed))
213
self.debug('update => [{status}] (elapsed time: {elapsed}, '
214
'retries left: {retries})'.format(status='OK' if updated else 'FAILED',
199
- elapsed=job.ELAPSED if updated else '-',
200
- retries=job.RETRIES_MAX - job.RETRIES))
215
+ elapsed=job.elapsed if updated else '-',
216
+ retries=job.max_retries - job.retries))
217
218
def update(self, interval):
219
"""
@@ -233,27 +249,6 @@ class SimpleService(Thread, PythonDLimitedLogger, OldVersionCompatibility, objec
249
250
return updated
251
236
- def manage_retries(self):
237
- rc = self._runtime_counters
238
- rc.RETRIES += 1
239
- if rc.RETRIES % 5 == 0:
240
- rc.PENALTY = int(rc.RETRIES * self.update_every / 2)
241
- if rc.RETRIES >= rc.RETRIES_MAX:
242
- self.error('stopped after {0} data collection failures in a row'.format(rc.RETRIES_MAX))
243
- return False
244
- return True
245
-
246
- def sleep_until_next_run(self):
247
- job = self._runtime_counters
248
-
249
- # sleep() is interruptable
250
- while job.is_sleep_time():
251
- sleep_time = job.NEXT_RUN - job.START_RUN
252
- self.debug('sleeping for {sleep_time} to reach frequency of {freq} sec'.format(sleep_time=sleep_time,
253
- freq=job.FREQ + job.PENALTY))
254
- sleep(sleep_time)
255
- job.START_RUN = monotonic()
256
-
252
def get_data(self):
253
return self._get_data()
254
collectors/python.d.plugin/python_modules/bases/charts.py
+1
-1
@@ -45,7 +45,7 @@ def create_runtime_chart(func):
45
ok = func(*args, **kwargs)
46
if ok:
47
safe_print(RUNTIME_CHART_CREATE.format(job_name=self.name,
48
- update_every=self._runtime_counters.FREQ))
48
+ update_every=self._runtime_counters.update_every))
49
return ok
50
return wrapper
51
collectors/python.d.plugin/python_modules/bases/loggers.py
+1
-1
@@ -34,7 +34,7 @@ def limiter(log_max_count=30, allowed_in_seconds=60):
34
def on_decorator(func):
35
36
def on_call(*args):
37
- current_time = args[0]._runtime_counters.START_RUN
37
+ current_time = args[0]._runtime_counters.start_mono
38
lc = args[0]._logger_counters
39
40
if lc.logged and lc.logged % log_max_count == 0: