summaryrefslogtreecommitdiff
path: root/zuul/web/__init__.py
diff options
context:
space:
mode:
authorMatthieu Huin <mhuin@redhat.com>2022-03-07 18:32:00 +0100
committerMatthieu Huin <mhuin@redhat.com>2022-03-24 15:30:32 +0100
commit1bd6a1e92fb6f8f251854a230eed37af5afe72bf (patch)
tree66103f9dd9270900ec3878d27621df1e4dbf88ed /zuul/web/__init__.py
parenta03d9fc3970a4812ac2e1c2c3f4d4cdf41ff9825 (diff)
downloadzuul-1bd6a1e92fb6f8f251854a230eed37af5afe72bf.tar.gz
REST API: cache tenants list
Queue size computations can be costly on big tenants. Add caching to limit performance impact. Change-Id: I101fc42e29f4b376272c417ec536c17f05cb1a60
Diffstat (limited to 'zuul/web/__init__.py')
-rwxr-xr-xzuul/web/__init__.py65
1 files changed, 48 insertions, 17 deletions
diff --git a/zuul/web/__init__.py b/zuul/web/__init__.py
index 7281f3b42..1fa36c98a 100755
--- a/zuul/web/__init__.py
+++ b/zuul/web/__init__.py
@@ -360,11 +360,20 @@ class ZuulWebAPI(object):
self.system = ZuulSystem(self.zk_client)
self.zk_nodepool = ZooKeeperNodepool(self.zk_client,
enable_node_cache=True)
- self.cache = {}
- self.cache_time = {}
+ self.caches = {
+ 'status': {
+ 'cache': {},
+ 'cache_time': {},
+ 'lock': defaultdict(threading.Lock)
+ },
+ 'tenants': {
+ 'cache': [],
+ 'cache_time': 0,
+ 'lock': threading.Lock()
+ },
+ }
self.cache_expiry = 1
self.static_cache_expiry = zuulweb.static_cache_expiry
- self.status_lock = defaultdict(threading.Lock)
@property
def log(self):
@@ -940,9 +949,7 @@ class ZuulWebAPI(object):
return [n for n, t in self.zuulweb.abide.tenants.items()
if self._is_authorized(t, claims)]
- @cherrypy.expose
- @cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
- def tenants(self):
+ def _tenants(self):
result = []
for tenant_name, tenant in sorted(self.zuulweb.abide.tenants.items()):
queue_size = 0
@@ -959,10 +966,26 @@ class ZuulWebAPI(object):
'projects': len(tenant.untrusted_projects),
'queue': queue_size,
})
+ return result
+
+ @cherrypy.expose
+ @cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
+ def tenants(self):
+ cache_time = self.caches['tenants']['cache_time']
+ if time.time() - cache_time > self.cache_expiry:
+ with self.caches['tenants']['lock']:
+ self.caches['tenants']['cache'] = self._tenants()
+ self.caches['tenants']['cache_time'] = time.time()
resp = cherrypy.response
+ resp.headers["Cache-Control"] = f"public, max-age={self.cache_expiry}"
+ last_modified = datetime.utcfromtimestamp(
+ self.caches['tenants']['cache_time']
+ )
+ last_modified_header = last_modified.strftime('%a, %d %b %Y %X GMT')
+ resp.headers["Last-modified"] = last_modified_header
resp.headers['Access-Control-Allow-Origin'] = '*'
- return result
+ return self.caches['tenants']['cache']
@cherrypy.expose
@cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
@@ -991,25 +1014,33 @@ class ZuulWebAPI(object):
def _getStatus(self, tenant_name):
tenant = self._getTenantOrRaise(tenant_name)
- if tenant_name not in self.cache or \
- (time.time() - self.cache_time[tenant_name]) > self.cache_expiry:
- if self.status_lock[tenant_name].acquire(blocking=False):
+ cache_times = self.caches['status']['cache_time']
+ cache_time = cache_times.get(tenant_name, 0)
+ if tenant_name not in self.caches['status']['lock'] or \
+ (time.time() - cache_time) > self.cache_expiry:
+ if self.caches['status']['lock'][tenant_name].acquire(
+ blocking=False
+ ):
try:
- self.cache[tenant_name] = self.formatStatus(tenant)
- self.cache_time[tenant_name] = time.time()
+ self.caches['status']['cache'][tenant_name] =\
+ self.formatStatus(tenant)
+ self.caches['status']['cache_time'][tenant_name] =\
+ time.time()
finally:
- self.status_lock[tenant_name].release()
- if not self.cache.get(tenant_name):
+ self.caches['status']['lock'][tenant_name].release()
+ if not self.caches['status']['cache'].get(tenant_name):
# If the cache is empty at this point it means that we didn't
# get the lock but another thread is initializing the cache
# for the first time. In this case we just wait for the lock
# to wait for it to finish.
- with self.status_lock[tenant_name]:
+ with self.caches['status']['lock'][tenant_name]:
pass
- payload = self.cache[tenant_name]
+ payload = self.caches['status']['cache'][tenant_name]
resp = cherrypy.response
resp.headers["Cache-Control"] = f"public, max-age={self.cache_expiry}"
- last_modified = datetime.utcfromtimestamp(self.cache_time[tenant_name])
+ last_modified = datetime.utcfromtimestamp(
+ self.caches['status']['cache_time'][tenant_name]
+ )
last_modified_header = last_modified.strftime('%a, %d %b %Y %X GMT')
resp.headers["Last-modified"] = last_modified_header
resp.headers['Access-Control-Allow-Origin'] = '*'