-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#119: Implement client object pool using LRU cache to hold 'in use' …
…clients - LRU cache allows recently used client objects to be kept around. This means there's a 1 client object to 1 session ID ratio, more resource sensitive than a 1 client object for every request ratio - Workflow: client pool is created at startup, incoming request fetches client from LRU cache, which pulls a client from the pool. Client is kept in the cache until it becomes least recently used, at which point it's then put back into the pool - POST to /sessions uses the same workflow, passing a (pretend) session ID of None to get the same client from the cache each time - The first time a new session ID is passed in the request headers, there is no slowdown in the request like before (where a client was being created for that ID). The client is fetched from the pool - I've ran this commit against some e2e tests on the frontend and the performance from the API was good, similar to the branch which uses a single client object, passed around using kwargs. I have no concerns regarding a 'slow API' with the pool and cache - This is just a rough proof of concept, there's lots of cleaning up to do, including making the resource stats on the pool accurate and not just passing the default ones each time. One potential solution is to make something similar to the `Executor` class in the pool library. I experiemented with this (think the class I mocked up is in this commit?) but I wanted to get a basic example working before worrying about the stats (which the API doesn't make use of, but it might be useful to keep accurate stats if they ever need to be logged out. All part of the cleanup process - This doesn't use the context manager as this wouldn't allow me to implement the LRU cache in the way I have
- Loading branch information
1 parent
fb20095
commit 74f1edd
Showing
9 changed files
with
200 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import logging | ||
|
||
from cachetools.func import _cache | ||
from cachetools.lru import LRUCache | ||
|
||
from icat.client import Client | ||
from object_pool import ObjectPool | ||
|
||
from datagateway_api.common.config import config | ||
|
||
log = logging.getLogger() | ||
|
||
|
||
class ICATClient(Client): | ||
"""Wrapper class to allow an object pool of client objects to be created""" | ||
|
||
def __init__(self): | ||
super().__init__(config.get_icat_url(), checkCert=config.get_icat_check_cert()) | ||
self.autoLogout = False | ||
|
||
def clean_up(self): | ||
""" | ||
Allows object pool to cleanup the client's resources, using the existing Python | ||
ICAT functionality | ||
""" | ||
super().cleanup() | ||
|
||
|
||
def create_client_pool(): | ||
return ObjectPool( | ||
ICATClient, min_init=5, max_capacity=20, max_reusable=0, expires=0, | ||
) | ||
|
||
|
||
class ClientPoolExecutor(ObjectPool.Executor): | ||
"""TODO""" | ||
|
||
def __init__(self, klass): | ||
# klass is the instance of object pool | ||
self.__pool = klass | ||
self.client, self.resource_stats = None | ||
|
||
def get_client(self): | ||
self.client, self.resource_stats = self.__pool._get_resource() | ||
return self.client | ||
|
||
def release_client(self): | ||
self.__pool._queue_resource(self.client, self.resource_stats) | ||
|
||
|
||
def get_executor(client_pool): | ||
return ClientPoolExecutor(client_pool) | ||
|
||
|
||
class ExtendedLRUCache(LRUCache): | ||
def __init__(self): | ||
super().__init__(maxsize=8) | ||
|
||
def popitem(self): | ||
key, client = super().popitem() | ||
session_id, client_pool = key | ||
log.debug(f"Item popped from LRU cache: {key}, {client}") | ||
# Put client back into pool | ||
# Passes in default stats for now, though these aren't used in the API | ||
client_pool._queue_resource(client, client_pool._get_default_stats()) | ||
|
||
|
||
def my_lru_cache(): | ||
return _cache(ExtendedLRUCache()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.