update icarus archiver to python3#53
Conversation
mattiasotgia
left a comment
There was a problem hiding this comment.
I left some suggestion on where some try..catch blocks might help
However, the top try:...catch block in the __main__ code should catch all the exceptions thrown by the program
| def LoadConfig(Name): | ||
| with open(Name) as JSONFile: |
There was a problem hiding this comment.
I suggest also in this place to add a try...except to catch any failure in reading
try:
with open(Name) as JSONFile:
Config = json.load(JSONFile)
except Exeption as e:
loggin.error('Failed to load Archiver configuration: {}'.format(e))
print('Failed to load Archiver configuration: {}'.format(e))
sys.exit(0)| ProcessCount = int(args.processes) | ||
| ProcessList = [] | ||
| #Start the Monitor process, which monitors CPU usage, memory, and sends a heartbeat to Redis. | ||
| Process(target=Monitor, args=(r, Config)).start() |
There was a problem hiding this comment.
Maybe add here a try...except to catch any exception starting process
try:
Process(target=Monitor, args=(r, Config)).start()
except Exception as e:
print('WARNING: Process not started: {}'.format(e))
logging.warning('Process not started: {}'.format(e))| ProcessList.append(Process(target=ProcessStreams, args=(r, p, cur, stream_last, per_stream_cfg, args))) | ||
|
|
||
| for i in range(ProcessCount): | ||
| ProcessList[i].start() |
There was a problem hiding this comment.
Same as above, add a try...except to catch exceptions
try:
ProcessList[i].start()
except Exception as e:
print('WARNING: Process not started: {}'.format(e))
logging.warning('Process not started: {}'.format(e))| if countStreams % 5000 == 0 or countStreams == totalStreams: | ||
| percentDone = (countStreams / totalStreams) * 100 | ||
| logging.info("Processed {}/{} streams ({:.2f}%)".format(countStreams,totalStreams,percentDone)) | ||
| countStreams += 1 |
There was a problem hiding this comment.
Maybe add in the archiver configuration the option to select the level of detail of the progress
if countStreams % ArcConfig['logging'].get('print_every', 5000) == 0 or countStreams == totalStreams:The default (if not present in the configuration json file) is 5000
| return ParseBinary(val,key)[0] | ||
| if key == b'val' or key == b'dat': | ||
| return val | ||
| type_name = key.decode('utf-8') |
There was a problem hiding this comment.
Add try...except here
try:
type_name = key.decode('utf-8')
except Exception as e:
print('WARNING: decoding error: {}'.format(e))
logging.warning('Decoding error: {}'.format(e))|
|
||
| MetricDict = { x : [] for x in StreamDict.keys()} | ||
|
|
||
| logging.info('Archiver in ProcessStreams.') | ||
| first_key = next(iter(StreamDict)) if StreamDict else None | ||
| if first_key is not None: | ||
| logging.info('LatestCompleted for ' + first_key + ': ' + StreamDict[first_key]) | ||
| else: | ||
| logging.info('No streams assigned to this process.') | ||
| return | ||
|
|
||
| ReconnectCount = 0 | ||
|
|
||
| #We now begin to continuously query these streams for new metrics, archiving them when possible. | ||
| while True: | ||
| TotalSetTime = 0.0 | ||
| TotalSetN = 0 | ||
| #ReadStream is a list of stream object which have new entries that have yet to be archived. | ||
| ReadStream = r.xread(StreamDict, block=0) | ||
| try: | ||
| st = time.time() | ||
| try: | ||
| ReadStream = r.xread(StreamDict, block=0) | ||
| except Exception as e: | ||
| logging.error(e) | ||
| logging.info('XREAD time: {}'.format(time.time()-st)) | ||
| logging.info('Redis READ completed.') | ||
| except redis.RedisError as err: | ||
| logging.error('Error while reading streams: {}'.format(err)) | ||
| logging.error(type(err)) | ||
| if ReconnectCount == 4: | ||
| sys.exit(0) | ||
| else: | ||
| ReconnectCount += 1 | ||
| logging.error('Reconnecting to Redis (Attempt ' + str(ReconnectCount) + ') ...') | ||
| ArcConfig = LoadConfig('ArchiverConfig.json') | ||
| r = ConnectRedis(ArcConfig, args) | ||
| continue | ||
|
|
||
| if len(ReadStream) == 0: | ||
| logging.error('Block timeout. Reconnecting to Redis...') | ||
| ArcConfig = LoadConfig('ArchiverConfig.json') | ||
| r = ConnectRedis(ArcConfig, args) | ||
| continue | ||
|
|
||
| ReconnectCount = 0 | ||
|
|
||
| #Loop over the streams which have entries to be archived. | ||
| countStreams = 0 | ||
| totalStreams = len(ReadStream) | ||
| logging.info("Streams to be archived: {}".format(totalStreams)) | ||
|
|
||
| for StreamObject in ReadStream: | ||
| # stream_name_b = StreamObject[0] | ||
| # print("DEBUG: stream_name_b =", stream_name_b, "type =", type(stream_name_b)) | ||
| # stream_name = stream_name_b.decode('utf-8') | ||
| stream_name = StreamObject[0] | ||
| entries = StreamObject[1] | ||
| logging.debug("Stream: {}".format(stream_name)) | ||
|
|
||
| #Loop over the individual entries in the stream. | ||
| for DataObject in StreamObject[1]: | ||
| #Check if the latest completed archived entry is '0' (no metrics have been archived yet for the stream). | ||
| if StreamDict[StreamObject[0]] == '0': | ||
| NewLatest = str( int( DataObject[0].split('-')[0] ) - 1 ) + '-0' | ||
| SetLatest(r, StreamObject[0], NewLatest) | ||
| ProcessData(MetricDict, DataObject, Config, StreamObject[0]) | ||
| StreamDict[StreamObject[0]] = DataObject[0] | ||
| #Check if the current entry is within the time block (contained in the interval of entries to be archived). | ||
| elif int( DataObject[0].split('-')[0] ) < int( GetLatest(r, StreamObject[0]).split('-')[0] ) + 1000*Config[StreamObject[0]][3]: | ||
| ProcessData(MetricDict, DataObject, Config, StreamObject[0]) | ||
| StreamDict[StreamObject[0]] = DataObject[0] | ||
| #The entry is outside the current time block. Set a new latest completed and write to the database. | ||
| else: | ||
| #Case for "mean" averaging. | ||
| if Config[StreamObject[0]][3] == 0: | ||
| SetLatest(r, StreamObject[0], MetricDict[StreamObject[0]][0]) | ||
| WritePostgres(p, cur, Config[StreamObject[0]][4], Config[StreamObject[0]][2], MetricDict[StreamObject[0]][1], int(MetricDict[StreamObject[0]][0].split('-')[0])) | ||
| MetricDict[StreamObject[0]] = [] | ||
| #Case for "median" averaging. | ||
| elif Config[StreamObject[0]][3] == 1: | ||
| SetLatest(r, StreamObject[0], MetricDict[StreamObject[0]][-1][0]) | ||
| MetricList = [ x[1] for x in MetricDict[StreamObject[0]] ] | ||
| WritePostgres(p, cur, Config[StreamObject[0]][4], Config[StreamObject[0]][2], median(MetricList), int(MetricDict[StreamObject[0]][0].split('-')[0])) | ||
| MetricDict[StreamObject[0]] = [] | ||
| #Case for "mode" averaging. | ||
| elif Config[StreamObject[0]][3] == 2: | ||
| SetLatest(r, StreamObject[0], MetricDict[StreamObject[0]][0]) | ||
| WritePostgres(p, cur, Config[StreamObject[0]][4], Config[StreamObject[0]][2], MetricDict[StreamObject[0]][3], int(MetricDict[StreamObject[0]][0].split('-')[0])) | ||
| MetricDict[StreamObject[0]] = [] | ||
| #Case for "max" averaging. | ||
| elif Config[StreamObject[0]][3] == 3: | ||
| SetLatest(r, StreamObject[0], MetricDict[StreamObject[0]][0]) | ||
| WritePostgres(p, cur, Config[StreamObject[0]][4], Config[StreamObject[0]][2], MetricDict[StreamObject[0]][1], int(MetricDict[StreamObject[0]][0].split('-')[0])) | ||
| MetricDict[StreamObject[0]] = [] | ||
| #Case for "min" averaging. | ||
| elif Config[StreamObject[0]][3] == 4: | ||
| SetLatest(r, StreamObject[0], MetricDict[StreamObject[0]][0]) | ||
| WritePostgres(p, cur, Config[StreamObject[0]][4], Config[StreamObject[0]][2], MetricDict[StreamObject[0]][1], int(MetricDict[StreamObject[0]][0].split('-')[0])) | ||
| MetricDict[StreamObject[0]] = [] | ||
| #Case for "last" averaging. | ||
| elif Config[StreamObject[0]][3] == 5: | ||
| SetLatest(r, StreamObject[0], MetricDict[StreamObject[0]][0]) | ||
| WritePostgres(p, cur, Config[StreamObject[0]][4], Config[StreamObject[0]][2], MetricDict[StreamObject[0]][1], int(MetricDict[StreamObject[0]][0].split('-')[0])) | ||
| MetricDict[StreamObject[0]] = [] | ||
| #After performing the archiving on the previous block of data, we can now process the current entry. | ||
| ProcessData(MetricDict, DataObject, Config, StreamObject[0]) | ||
| StreamDict[StreamObject[0]] = DataObject[0] | ||
| #Check to see if there is a gap in the data stream (the current object is more than one time interval from the latest completed). | ||
| if int( DataObject[0].split('-')[0] ) > int( GetLatest(r, StreamObject[0]).split('-')[0] ) + 1000*Config[StreamObject[0]][3]: | ||
| NewLatest = str( int( DataObject[0].split('-')[0] ) - 1 ) + '-0' | ||
| SetLatest(r, StreamObject[0], NewLatest) | ||
| for DataObject in entries: | ||
| entry_id_b, entry_fields = DataObject | ||
| try: | ||
| entry_id = entry_id_b.decode('utf-8') | ||
|
|
||
| logging.debug("Entry: {}, fields: {}".format(entry_id,entry_fields)) | ||
|
|
||
| #Check if the latest completed archived entry is '0' (no metrics have been archived yet for the stream). | ||
| if StreamDict[stream_name] == '0': | ||
| NewLatest = str( int( entry_id.split('-')[0] ) - 1 ) + '-0' | ||
| TotalSetTime += SetLatest(r, stream_name, NewLatest) | ||
| TotalSetN += 1 | ||
| ProcessData(MetricDict, (entry_id, entry_fields), Config, stream_name) | ||
| StreamDict[stream_name] = entry_id | ||
| #Check if the current entry is within the time block (contained in the interval of entries to be archived). | ||
| elif int( entry_id.split('-')[0] ) < int( GetLatest(r, stream_name).split('-')[0] ) + 1000*Config[stream_name][5]: | ||
| ProcessData(MetricDict, (entry_id, entry_fields), Config, stream_name) | ||
| StreamDict[stream_name] = entry_id | ||
| #Make sure that there are metrics to archive (No metrics in MetricDict, but GetLatest is more than one time block previous to the next entry). | ||
| elif int( entry_id.split('-')[0] ) > int( GetLatest(r, stream_name).split('-')[0] ) + 1000*Config[stream_name][5] and len(MetricDict[stream_name]) == 0: | ||
| NewLatest = str( int( entry_id.split('-')[0] ) - 1 ) + '-0' | ||
| TotalSetTime += SetLatest(r, stream_name, NewLatest) | ||
| TotalSetN += 1 | ||
| ProcessData(MetricDict, (entry_id, entry_fields), Config, stream_name) | ||
| StreamDict[stream_name] = entry_id | ||
| #The entry is outside the current time block. Set a new latest completed and write to the database. | ||
| else: | ||
| #Case for "mean" averaging. | ||
| if Config[stream_name][3] == 0: | ||
| TotalSetTime += SetLatest(r, stream_name, MetricDict[stream_name][0]) | ||
| TotalSetN += 1 | ||
| WritePostgres(p, cur, Config[stream_name][4], Config[stream_name][2], MetricDict[stream_name][1], int(MetricDict[stream_name][0].split('-')[0])) | ||
| MetricDict[stream_name] = [] | ||
| #Case for "median" averaging. | ||
| elif Config[stream_name][3] == 1: | ||
| TotalSetTime += SetLatest(r, stream_name, MetricDict[stream_name][-1][0]) | ||
| TotalSetN += 1 | ||
| MetricList = [ x[1] for x in MetricDict[stream_name] ] | ||
| WritePostgres(p, cur, Config[stream_name][4], Config[stream_name][2], median(MetricList), int(MetricDict[stream_name][-1][0].split('-')[0])) | ||
| MetricDict[stream_name] = [] | ||
| #Case for "mode" averaging. | ||
| elif Config[stream_name][3] == 2: | ||
| TotalSetTime += SetLatest(r, stream_name, MetricDict[stream_name][0]) | ||
| TotalSetN += 1 | ||
| WritePostgres(p, cur, Config[stream_name][4], Config[stream_name][2], MetricDict[stream_name][3], int(MetricDict[stream_name][0].split('-')[0])) | ||
| MetricDict[stream_name] = [] | ||
| #Case for "max" averaging. | ||
| elif Config[stream_name][3] == 3: | ||
| TotalSetTime += SetLatest(r, stream_name, MetricDict[stream_name][0]) | ||
| TotalSetN += 1 | ||
| WritePostgres(p, cur, Config[stream_name][4], Config[stream_name][2], MetricDict[stream_name][1], int(MetricDict[stream_name][0].split('-')[0])) | ||
| MetricDict[stream_name] = [] | ||
| #Case for "min" averaging. | ||
| elif Config[stream_name][3] == 4: | ||
| TotalSetTime += SetLatest(r, stream_name, MetricDict[stream_name][0]) | ||
| TotalSetN += 1 | ||
| WritePostgres(p, cur, Config[stream_name][4], Config[stream_name][2], MetricDict[stream_name][1], int(MetricDict[stream_name][0].split('-')[0])) | ||
| MetricDict[stream_name] = [] | ||
| #Case for "last" averaging. | ||
| elif Config[stream_name][3] == 5: | ||
| TotalSetTime += SetLatest(r, stream_name, MetricDict[stream_name][0]) | ||
| WritePostgres(p, cur, Config[stream_name][4], Config[stream_name][2], MetricDict[stream_name][1], int(MetricDict[stream_name][0].split('-')[0])) | ||
| MetricDict[stream_name] = [] | ||
| #After performing the archiving on the previous block of data, we can now process the current entry. | ||
| ProcessData(MetricDict, (entry_id, entry_fields), Config, stream_name) | ||
| StreamDict[stream_name] = entry_id | ||
| #Check to see if there is a gap in the data stream (the current object is more than one time interval from the latest completed). | ||
| if int( entry_id.split('-')[0] ) > int( GetLatest(r, stream_name).split('-')[0] ) + 1000*Config[stream_name][5]: | ||
| NewLatest = str( int( entry_id.split('-')[0] ) - 1 ) + '-0' | ||
| TotalSetTime += SetLatest(r, stream_name, NewLatest) | ||
| TotalSetN += 1 | ||
|
|
||
| except Exception as e: | ||
| logging.exception(e) | ||
|
|
||
| if countStreams % 5000 == 0 or countStreams == totalStreams: | ||
| percentDone = (countStreams / totalStreams) * 100 | ||
| logging.info("Processed {}/{} streams ({:.2f}%)".format(countStreams,totalStreams,percentDone)) | ||
| countStreams += 1 | ||
|
|
||
| logging.info('Total set time: {}'.format(TotalSetTime)) | ||
| logging.info('Average set time: {}'.format(float(TotalSetTime)/float(TotalSetN) if TotalSetN else 0.0)) |
There was a problem hiding this comment.
Put all the content of the ProcessStreams inside a try...catch block
| logging.info('Monitor is running.') | ||
| Time = time.time() | ||
| while True: | ||
| ErrorCount = 0 | ||
| while ErrorCount < 4: | ||
| pipeline = r.pipeline() | ||
| if time.time() >= Time + 60: | ||
| pipeline.xadd('archiver_heartbeat', {'float': struct.pack('f', 0)}, maxlen=1) | ||
| Time = time.time() | ||
| CPUs = psutil.cpu_percent(interval=Config['redis']['monitor_time'], percpu=True) | ||
| CPUAverage = psutil.cpu_percent(interval=Config['redis']['monitor_time']) | ||
| Mem = psutil.virtual_memory()[2] | ||
| for i in range(len(CPUs)): pipeline.xadd('archiver_cpu'+str(i), {'float': struct.pack('f', CPUs[i])}) | ||
| pipeline.xadd('archiver_cpu_average', {'float': struct.pack('f', CPUAverage)}) | ||
| pipeline.xadd('archiver_mem', {'float': struct.pack('f', Mem)}) | ||
| [ _ for _ in pipeline.execute()] | ||
| #pipeline.xadd('archiver_heartbeat', {'float': struct.pack('f', 0)}, maxlen=1) | ||
| #CPUs = psutil.cpu_percent(interval=Config['redis']['monitor_time'], percpu=True) | ||
| #CPUAverage = psutil.cpu_percent(interval=Config['redis']['monitor_time']) | ||
| #Mem = psutil.virtual_memory()[2] | ||
| #try: | ||
| # for i in range(len(CPUs)): pipeline.xadd('archiver_cpu'+str(i), {'float': struct.pack('f', CPUs[i])}, maxlen=10) | ||
| # pipeline.xadd('archiver_cpu_average', {'float': struct.pack('f', CPUAverage)}, maxlen=10) | ||
| # pipeline.xadd('archiver_mem', {'float': struct.pack('f', Mem)}, maxlen=10) | ||
| # [ _ for _ in pipeline.execute()] | ||
| #except redis.RedisError as err: | ||
| # logging.error(str('Error while executing pipeline for archiver metrics: {}').format(err)) | ||
| # sys.exit(0) | ||
| #time.sleep(300) | ||
| try: | ||
| pipeline.xadd('archiver_heartbeat', {'float': struct.pack('f', 0.0)}, maxlen=1) | ||
| [ _ for _ in pipeline.execute() ] | ||
| except redis.RedisError as err: | ||
| logging.error(str('Error while executing pipeline for archiver heartbeat: {}').format(err)) | ||
| #sys.exit(0) | ||
| ErrorCount += 1 | ||
| time.sleep(60) |
There was a problem hiding this comment.
Put all the content of the Monitor inside of a try...except block
This PR does the following: