Browse Source

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
tags/3.4.0
Tobias Henkel 6 months ago
parent
commit
6eb80deb36
No account linked to committer's email address
1 changed files with 58 additions and 13 deletions
  1. 58
    13
      nodepool/zk.py

+ 58
- 13
nodepool/zk.py View File

@@ -703,6 +703,7 @@ class ZooKeeper(object):
703 703
         self._node_cache = None
704 704
         self._request_cache = None
705 705
         self._cached_nodes = {}
706
+        self._cached_node_requests = {}
706 707
 
707 708
     # =======================================================================
708 709
     # Private Methods
@@ -899,6 +900,8 @@ class ZooKeeper(object):
899 900
             self._node_cache.start()
900 901
 
901 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 905
             self._request_cache.start()
903 906
 
904 907
     def disconnect(self):
@@ -1569,25 +1572,21 @@ class ZooKeeper(object):
1569 1572
 
1570 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 1575
         if cached:
1576
-            cached_data = self._request_cache.get_data(path)
1577
-            if cached_data:
1578
-                data = cached_data.data
1579
-                stat = cached_data.stat
1576
+            d = self._cached_node_requests.get(request)
1577
+            if d:
1578
+                return d
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 1581
         # have the request (yet). Note that even if we use caching we need to
1583 1582
         # do a real query if the cached data is empty because the request data
1584 1583
         # might not be in the cache yet when it's listed by the get_children
1585 1584
         # call.
1586
-        if not data:
1587
-            try:
1588
-                data, stat = self.client.get(path)
1589
-            except kze.NoNodeError:
1590
-                return None
1585
+        try:
1586
+            path = self._requestPath(request)
1587
+            data, stat = self.client.get(path)
1588
+        except kze.NoNodeError:
1589
+            return None
1591 1590
 
1592 1591
         d = NodeRequest.fromDict(self._bytesToDict(data), request)
1593 1592
         d.stat = stat
@@ -2107,3 +2106,49 @@ class ZooKeeper(object):
2107 2106
             except KeyError:
2108 2107
                 # If it's already gone, don't care
2109 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

Loading…
Cancel
Save