summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Henkel <tobias.henkel@bmw.de>2018-11-20 20:21:54 +0100
committerTobias Henkel <tobias.henkel@bmw.de>2018-11-26 20:10:35 +0100
commit6eb80deb36ba3f1c6975ce321ef1c391905b661e (patch)
treea590f19e51016b1018088dc5dc5ea02790f81b53
parent35094dbb62f273bfdb64ed3c9f0ac2336c87f7f3 (diff)
Add second level cache to node requests
When implementing dynamic node request priorities we need to be able to quickly get all requests and reorder them in a priority queue. This won't be efficient if this involves excessive json parsing. So this adds an event based second level cache. Change-Id: I923195de1890fdb74f7e5b33a0165f400dbbf374
Notes
Notes (review): Code-Review+2: James E. Blair <corvus@inaugust.com> Code-Review+2: David Shrewsbury <shrewsbury.dave@gmail.com> Workflow+1: David Shrewsbury <shrewsbury.dave@gmail.com> Verified+2: Zuul Submitted-by: Zuul Submitted-at: Thu, 29 Nov 2018 09:59:40 +0000 Reviewed-on: https://review.openstack.org/619069 Project: openstack-infra/nodepool Branch: refs/heads/master
-rwxr-xr-xnodepool/zk.py71
1 files changed, 58 insertions, 13 deletions
diff --git a/nodepool/zk.py b/nodepool/zk.py
index 6ce42e4..d67342c 100755
--- a/nodepool/zk.py
+++ b/nodepool/zk.py
@@ -703,6 +703,7 @@ class ZooKeeper(object):
703 self._node_cache = None 703 self._node_cache = None
704 self._request_cache = None 704 self._request_cache = None
705 self._cached_nodes = {} 705 self._cached_nodes = {}
706 self._cached_node_requests = {}
706 707
707 # ======================================================================= 708 # =======================================================================
708 # Private Methods 709 # Private Methods
@@ -899,6 +900,8 @@ class ZooKeeper(object):
899 self._node_cache.start() 900 self._node_cache.start()
900 901
901 self._request_cache = TreeCache(self.client, self.REQUEST_ROOT) 902 self._request_cache = TreeCache(self.client, self.REQUEST_ROOT)
903 self._request_cache.listen_fault(self.cacheFaultListener)
904 self._request_cache.listen(self.requestCacheListener)
902 self._request_cache.start() 905 self._request_cache.start()
903 906
904 def disconnect(self): 907 def disconnect(self):
@@ -1569,25 +1572,21 @@ class ZooKeeper(object):
1569 1572
1570 :returns: The request data, or None if the request was not found. 1573 :returns: The request data, or None if the request was not found.
1571 ''' 1574 '''
1572 path = self._requestPath(request)
1573 data = None
1574 stat = None
1575 if cached: 1575 if cached:
1576 cached_data = self._request_cache.get_data(path) 1576 d = self._cached_node_requests.get(request)
1577 if cached_data: 1577 if d:
1578 data = cached_data.data 1578 return d
1579 stat = cached_data.stat
1580 1579
1581 # If data is empty we either didn't use the cache or the cache didn't 1580 # If we got here we either didn't use the cache or the cache didn't
1582 # have the request (yet). Note that even if we use caching we need to 1581 # have the request (yet). Note that even if we use caching we need to
1583 # do a real query if the cached data is empty because the request data 1582 # do a real query if the cached data is empty because the request data
1584 # might not be in the cache yet when it's listed by the get_children 1583 # might not be in the cache yet when it's listed by the get_children
1585 # call. 1584 # call.
1586 if not data: 1585 try:
1587 try: 1586 path = self._requestPath(request)
1588 data, stat = self.client.get(path) 1587 data, stat = self.client.get(path)
1589 except kze.NoNodeError: 1588 except kze.NoNodeError:
1590 return None 1589 return None
1591 1590
1592 d = NodeRequest.fromDict(self._bytesToDict(data), request) 1591 d = NodeRequest.fromDict(self._bytesToDict(data), request)
1593 d.stat = stat 1592 d.stat = stat
@@ -2107,3 +2106,49 @@ class ZooKeeper(object):
2107 except KeyError: 2106 except KeyError:
2108 # If it's already gone, don't care 2107 # If it's already gone, don't care
2109 pass 2108 pass
2109
2110 def requestCacheListener(self, event):
2111
2112 if hasattr(event.event_data, 'path'):
2113 # Ignore root node
2114 path = event.event_data.path
2115 if path == self.REQUEST_ROOT:
2116 return
2117
2118 # Ignore lock nodes
2119 if '/lock' in path:
2120 return
2121
2122 # Ignore any non-node related events such as connection events here
2123 if event.event_type not in (TreeEvent.NODE_ADDED,
2124 TreeEvent.NODE_UPDATED,
2125 TreeEvent.NODE_REMOVED):
2126 return
2127
2128 path = event.event_data.path
2129 request_id = path.rsplit('/', 1)[1]
2130
2131 if event.event_type in (TreeEvent.NODE_ADDED, TreeEvent.NODE_UPDATED):
2132 # Perform an in-place update of the cached request if possible
2133 d = self._bytesToDict(event.event_data.data)
2134 old_request = self._cached_node_requests.get(request_id)
2135 if old_request:
2136 if event.event_data.stat.version <= old_request.stat.version:
2137 # Don't update to older data
2138 return
2139 if old_request.lock:
2140 # Don't update a locked node request
2141 return
2142 old_request.updateFromDict(d)
2143 old_request.stat = event.event_data.stat
2144 else:
2145 request = NodeRequest.fromDict(d, request_id)
2146 request.stat = event.event_data.stat
2147 self._cached_node_requests[request_id] = request
2148
2149 elif event.event_type == TreeEvent.NODE_REMOVED:
2150 try:
2151 del self._cached_node_requests[request_id]
2152 except KeyError:
2153 # If it's already gone, don't care
2154 pass