mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
6a88dc3d6a
Implement a downstream load/save handler in `revng.internal.api.Manager` class which handles loading and saving files from a local directory to a s3 server.
93 lines
3.0 KiB
Python
93 lines
3.0 KiB
Python
#
|
|
# This file is distributed under the MIT License. See LICENSE.md for details.
|
|
#
|
|
|
|
import time
|
|
from enum import StrEnum, auto
|
|
from functools import wraps
|
|
from threading import Lock, Thread
|
|
from typing import Callable, Iterable, cast
|
|
|
|
from revng.internal.api import Manager
|
|
|
|
|
|
class EventType(StrEnum):
|
|
BEGIN = auto()
|
|
CONTEXT = auto()
|
|
PRODUCE = auto()
|
|
|
|
|
|
def emit_event(event_type: EventType):
|
|
"""Decorator for allowing a GraphQL handler to emit an event after handling.
|
|
This is expected to wrap handler-like function where the second argument is
|
|
GraphQL's info object"""
|
|
|
|
def decorator(f):
|
|
@wraps(f)
|
|
async def inner(*args, **kwargs):
|
|
result = await f(*args, **kwargs)
|
|
context = args[1].context
|
|
if "event_manager" in context:
|
|
context["event_manager"].handle_event(event_type)
|
|
return result
|
|
|
|
return inner
|
|
|
|
return decorator
|
|
|
|
|
|
class EventManager(Thread):
|
|
def __init__(self, manager: Manager, save_hooks: Iterable[Callable[[Manager, str], None]]):
|
|
super().__init__()
|
|
# Read-only fields, these are never written after __init__
|
|
self._interval = 120 # 2 minutes
|
|
self.manager = manager
|
|
self.save_hooks = save_hooks
|
|
self.running = True
|
|
|
|
self.lock = Lock()
|
|
# Writable fields, when writing to them use self.lock
|
|
self.last_save: float = time.time()
|
|
self.last_event: float | None = None
|
|
self.credentials: str | None = None
|
|
|
|
def run(self):
|
|
while self.running:
|
|
# This loop will call manager.save every 2 minutes if changes have been made in the
|
|
# meantime. This is to prevent the manager from lagging on every change. Some changes
|
|
# related to speeding up the pipeline manager from saving (e.g. detecting dirtiness in
|
|
# containers) need to be implemented before this can be dropped
|
|
if self.last_save + self._interval < time.time() and (
|
|
self.last_event is not None and self.last_event > self.last_save
|
|
):
|
|
self.save()
|
|
time.sleep(10)
|
|
|
|
def save(self) -> bool:
|
|
"""Save the manager and trigger the hooks. This method needs to be
|
|
thread-save because it might be called from both this class' run method
|
|
(which is executed in a separate thread) and the GraphQL API."""
|
|
result = self.manager.save()
|
|
if not result:
|
|
return False
|
|
|
|
with self.lock:
|
|
self.last_save = time.time()
|
|
# Copy the credentials to avoid them changing while running
|
|
# the hooks
|
|
credentials = self.credentials
|
|
|
|
for hook in self.save_hooks:
|
|
hook(self.manager, cast(str, credentials))
|
|
|
|
return result
|
|
|
|
def handle_event(self, type_: EventType):
|
|
with self.lock:
|
|
self.last_event = time.time()
|
|
|
|
def set_credentials(self, credentials: str):
|
|
with self.lock:
|
|
self.credentials = credentials
|
|
self.manager.set_storage_credentials(credentials)
|