summaryrefslogtreecommitdiffstats
path: root/khashmir/kstore.py
diff options
context:
space:
mode:
Diffstat (limited to 'khashmir/kstore.py')
-rw-r--r--khashmir/kstore.py119
1 files changed, 119 insertions, 0 deletions
diff --git a/khashmir/kstore.py b/khashmir/kstore.py
new file mode 100644
index 0000000..5c01200
--- /dev/null
+++ b/khashmir/kstore.py
@@ -0,0 +1,119 @@
+# The contents of this file are subject to the BitTorrent Open Source License
+# Version 1.1 (the License). You may not copy or use this file, in either
+# source code or executable form, except in compliance with the License. You
+# may obtain a copy of the License at http://www.bittorrent.com/license/.
+#
+# Software distributed under the License is distributed on an AS IS basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+
+try:
+ from random import sample
+except ImportError:
+ from random import choice
+ def sample(l, n):
+ if len(l) <= n:
+ return l
+ d = {}
+ while len(d) < n:
+ d[choice(l)] = 1
+ return d.keys()
+
+from BitTorrent.platform import bttime as time
+
+class KItem:
+ def __init__(self, key, value):
+ self.t = time()
+ self.k = key
+ self.v = value
+ def __cmp__(self, a):
+ # same value = same item, only used to keep dupes out of db
+ if self.v == a.v:
+ return 0
+
+ # compare by time
+ if self.t < a.t:
+ return -1
+ elif self.t > a.t:
+ return 1
+ else:
+ return 0
+
+ def __hash__(self):
+ return self.v.__hash__()
+
+ def __repr__(self):
+ return `(self.k, self.v, time() - self.t)`
+
+## in memory data store for distributed tracker
+## keeps a list of values per key in dictionary
+## keeps expiration for each key in a queue
+## can efficiently expire all values older than a given time
+## can insert one val at a time, or a list: ks['key'] = 'value' or ks['key'] = ['v1', 'v2', 'v3']
+class KStore:
+ def __init__(self):
+ self.d = {}
+ self.q = []
+
+ def __getitem__(self, key):
+ return [x.v for x in self.d[key]]
+
+ def __setitem__(self, key, value):
+ if type(value) == type([]):
+ [self.__setitem__(key, v) for v in value]
+ return
+ x = KItem(key, value)
+ try:
+ l = self.d[key]
+ except KeyError:
+ self.d[key] = [x]
+ else:
+ # this is slow
+ try:
+ i = l.index(x)
+ del(l[i])
+ except ValueError:
+ pass
+ l.insert(0, x)
+ self.q.append(x)
+
+ def __delitem__(self, key):
+ del(self.d[key])
+
+ def __len__(self):
+ return len(self.d)
+
+ def keys(self):
+ return self.d.keys()
+
+ def values(self):
+ return [self[key] for key in self.keys()]
+
+ def items(self):
+ return [(key, self[key]) for key in self.keys()]
+
+ def expire(self, t):
+ #.expire values inserted prior to t
+ try:
+ while self.q[0].t <= t:
+ x = self.q.pop(0)
+ try:
+ l = self.d[x.k]
+ try:
+ while l[-1].t <= t:
+ l.pop()
+ except IndexError:
+ del(self.d[x.k])
+ except KeyError:
+ pass
+ except IndexError:
+ pass
+
+ def sample(self, key, n):
+ # returns n random values of key, or all values if less than n
+ try:
+ l = [x.v for x in sample(self.d[key], n)]
+ except ValueError:
+ l = [x.v for x in self.d[key]]
+ return l