history: store job keys as zero padded hex values

This ensures that jobs in the database will be stored in the order they were started.  Doing so also eliminates the need for the `history_auto_inc_id", as we can initialize the next id from the database keys.

This also caches history keys in memory so we do not need to read them from the database on each access.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
Arksine 2021-03-13 06:49:35 -05:00
parent dd321a4754
commit 53ad4cb493
1 changed files with 44 additions and 45 deletions

View File

@ -5,17 +5,12 @@ import logging
import time import time
HIST_NAMESPACE = "history" HIST_NAMESPACE = "history"
JOBS_AUTO_INC_KEY = "history_auto_inc_id"
class History: class History:
def __init__(self, config): def __init__(self, config):
self.server = config.get_server() self.server = config.get_server()
self.database = self.server.lookup_plugin("database") database = self.server.lookup_plugin("database")
self.gcdb = self.database.wrap_namespace("gcode_metadata", self.gcdb = database.wrap_namespace("gcode_metadata", parse_keys=False)
parse_keys=False)
self.current_job = None
self.current_job_id = None
self.print_stats = {}
self.server.register_event_handler( self.server.register_event_handler(
"server:klippy_ready", self._init_ready) "server:klippy_ready", self._init_ready)
@ -32,12 +27,17 @@ class History:
self.server.register_endpoint( self.server.register_endpoint(
"/server/history/list", ['GET'], self._handle_jobs_list) "/server/history/list", ['GET'], self._handle_jobs_list)
self.database.register_local_namespace(HIST_NAMESPACE) database.register_local_namespace(HIST_NAMESPACE)
self.history_ns = self.database.wrap_namespace(HIST_NAMESPACE, self.history_ns = database.wrap_namespace(HIST_NAMESPACE,
parse_keys=False) parse_keys=False)
if JOBS_AUTO_INC_KEY not in self.database.ns_keys("moonraker"): self.current_job = None
self.database.insert_item("moonraker", JOBS_AUTO_INC_KEY, 0) self.current_job_id = None
self.print_stats = {}
self.next_job_id = 0
self.cached_job_ids = self.history_ns.keys()
if self.cached_job_ids:
self.next_job_id = int(self.cached_job_ids[-1], 16) + 1
async def _init_ready(self): async def _init_ready(self):
klippy_apis = self.server.lookup_plugin('klippy_apis') klippy_apis = self.server.lookup_plugin('klippy_apis')
@ -51,29 +51,27 @@ class History:
async def _handle_job_request(self, web_request): async def _handle_job_request(self, web_request):
action = web_request.get_action() action = web_request.get_action()
if action == "GET": if action == "GET":
id = web_request.get_str("id") job_id = web_request.get_str("uid")
if id not in self.history_ns: if job_id not in self.cached_job_ids:
raise self.server.error(f"Invalid job id: {id}", 404) raise self.server.error(f"Invalid job uid: {job_id}", 404)
job = self.history_ns[id] job = self.history_ns[job_id]
job['job_id'] = id job['job_id'] = job_id
return {"job": job} return {"job": job}
if action == "DELETE": if action == "DELETE":
all = web_request.get_boolean("all", False) all = web_request.get_boolean("all", False)
if all: if all:
deljobs = [] deljobs = self.cached_job_ids
for job in self.history_ns.keys(): self.history_ns.clear()
self.delete_job(job) self.cached_job_ids = []
deljobs.append(job) self.next_job_id = 0
self.database.insert_item("moonraker", JOBS_AUTO_INC_KEY, 0)
self.metadata = []
return {'deleted_jobs': deljobs} return {'deleted_jobs': deljobs}
id = web_request.get_str("id") job_id = web_request.get_str("uid")
if id not in self.history_ns.keys(): if job_id not in self.cached_job_ids:
raise self.server.error(f"Invalid job id: {id}", 404) raise self.server.error(f"Invalid job uid: {job_id}", 404)
self.delete_job(id) self.delete_job(job_id)
return {'deleted_jobs': [id]} return {'deleted_jobs': [job_id]}
async def _handle_jobs_list(self, web_request): async def _handle_jobs_list(self, web_request):
i = 0 i = 0
@ -88,8 +86,8 @@ class History:
if start >= end_num or end_num == 0: if start >= end_num or end_num == 0:
return {"count": 0, "jobs": {}} return {"count": 0, "jobs": {}}
for id in self.history_ns.keys(): for job_id in self.cached_job_ids:
job = self.history_ns[id] job = self.history_ns[job_id]
if since != -1 and since > job.get('start_time'): if since != -1 and since > job.get('start_time'):
start_num += 1 start_num += 1
continue continue
@ -101,7 +99,7 @@ class History:
if start != 0: if start != 0:
start -= 1 start -= 1
continue continue
job['job_id'] = id job['job_id'] = job_id
jobs.append(job) jobs.append(job)
i += 1 i += 1
@ -149,22 +147,22 @@ class History:
ps['state'] != "paused" ps['state'] != "paused"
def add_job(self, job): def add_job(self, job):
self.current_job_id = str( job_id = f"{self.next_job_id:06X}"
self.database.get_item("moonraker", JOBS_AUTO_INC_KEY))
self.database.insert_item("moonraker", JOBS_AUTO_INC_KEY,
int(self.current_job_id)+1)
self.current_job = job self.current_job = job
self.grab_job_metadata() self.grab_job_metadata()
self.history_ns[self.current_job_id] = job.get_stats() self.history_ns[job_id] = job.get_stats()
self.current_job_id = job_id
self.cached_job_ids.append(job_id)
self.next_job_id += 1
self.send_history_event("added") self.send_history_event("added")
def delete_job(self, id): def delete_job(self, job_id):
id = str(id) if isinstance(job_id, int):
job_id = f"{job_id:06X}"
if id in self.history_ns.keys(): if job_id in self.cached_job_ids:
del self.history_ns[id] del self.history_ns[job_id]
self.cached_job_ids.remove(job_id)
return
def finish_job(self, status, pstats): def finish_job(self, status, pstats):
if self.current_job is None: if self.current_job is None:
@ -178,9 +176,10 @@ class History:
self.current_job = None self.current_job = None
self.current_job_id = None self.current_job_id = None
def get_job(self, id): def get_job(self, job_id):
id = str(id) if isinstance(job_id, int):
return self.history_ns.get(id, None) job_id = f"{job_id:06X}"
return self.history_ns.get(job_id, None)
def grab_job_metadata(self): def grab_job_metadata(self):
if self.current_job is None: if self.current_job is None: