Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/olpc/datastore/datastore.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/olpc/datastore/datastore.py')
-rw-r--r--src/olpc/datastore/datastore.py145
1 files changed, 121 insertions, 24 deletions
diff --git a/src/olpc/datastore/datastore.py b/src/olpc/datastore/datastore.py
index 4ca13b6..0a37f49 100644
--- a/src/olpc/datastore/datastore.py
+++ b/src/olpc/datastore/datastore.py
@@ -17,13 +17,10 @@ import dbus.service
import dbus.mainloop.glib
from olpc.datastore import utils
+from olpc.datastore import lru
+from olpc.datastore.sxattr import Xattr
+from olpc.datastore.config import *
-# the name used by the logger
-DS_LOG_CHANNEL = 'org.laptop.sugar.DataStore'
-
-DS_SERVICE = "org.laptop.sugar.DataStore"
-DS_DBUS_INTERFACE = "org.laptop.sugar.DataStore"
-DS_OBJECT_PATH = "/org/laptop/sugar/DataStore"
logger = logging.getLogger(DS_LOG_CHANNEL)
@@ -36,6 +33,9 @@ class DataStore(dbus.service.Object):
self.backends = []
self.mountpoints = {}
self.root = None
+
+ # maps uids to the mountpoint of the tip revision
+ self._mpcache = lru.LRU(20)
# global handle to the main look
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
@@ -172,7 +172,7 @@ class DataStore(dbus.service.Object):
def _resolveMountpoint(self, mountpoint=None):
if isinstance(mountpoint, dict):
mountpoint = mountpoint.pop('mountpoint', None)
-
+
if mountpoint is not None:
# this should be the id of a mount point
mp = self.mountpoints[mountpoint]
@@ -181,6 +181,61 @@ class DataStore(dbus.service.Object):
mp = self.root
return mp
+
+ def _mountpointFor(self, uid):
+ # XXX: this is flawed in that we really need to resolve merge
+ # cases where objects exist in branches over two or more
+ # stores
+ # (and this have the same rev for different heads)
+
+ # first, is it in the LRU cache?
+ if uid in self._mpcache:
+ return self._mpcache[uid]
+
+ # attempt to resolve (and cache the mount point)
+ # this is the start of some merge code
+ on = []
+ for mp in self.mountpoints.itervalues():
+ try:
+ if "versions" in mp.capabilities:
+ c = mp.get(uid, allow_many=True)
+ if c: on.append((mp, c))
+ else:
+ c = mp.get(uid)
+ if c: on.append((mp, c))
+ except KeyError:
+ pass
+
+ if on:
+ # find the single most recent revision
+ def versionCmp(x, y):
+ mpa, ca = x # mp, content
+ mpb, cb = y
+ # first by revision
+ r = cmp(int(ca.get_property('vid')),
+ int(cb.get_property('vid')))
+ if r != 0: return r
+ # if they have the same revision we've detected a
+ # branch
+ # we should resolve the common parent in a merge case,
+ # etc.
+ # XXX: here we defer to time
+ return cmp(ca.get_property('mtime', 0), cb.get_property('mtime', 0))
+
+
+ if len(on) > 1:
+ on.sort(versionCmp)
+ # the first mount point should be the most recent
+ # revision
+ mp = on[0][0]
+ else:
+ # No store has this uid. Doesn't mean it doesn't exist,
+ # just that its not mounted
+ mp = None
+
+ self._mpcache[uid] = mp
+ return mp
+
# PUBLIC API
@dbus.service.method(DS_DBUS_INTERFACE,
in_signature='a{sv}s',
@@ -192,11 +247,27 @@ class DataStore(dbus.service.Object):
This method returns the uid and version id tuple.
"""
- mp = self._resolveMountpoint(props)
+ mp = None
+ # dbus cleaning
+ props = utils._convert(props)
+
+ if filelike is not None:
+ filelike = str(filelike)
+ if filelike:
+ # attempt to scan the xattr namespace for information that can
+ # allow us to process this request more quickly
+ x = Xattr(filelike, XATTR_NAMESPACE)
+ known = x.asDict()
+ if "mountpoint" not in props and "mountpoint" in known:
+ mp = self._resolveMountpoint(known['mountpoint'])
+
+ if not mp:
+ mp = self._resolveMountpoint(props)
+
if "versions" not in mp.capabilities:
- uid = props.get('uid')
vid = "1" # we don't care about vid on
# non-versioning stores
+ uid = props.get('uid')
if uid:
# this is an update operation
# and uid refers to the single object
@@ -208,8 +279,9 @@ class DataStore(dbus.service.Object):
else:
# use the unified checkin on the backingstore
uid, vid = mp.checkin(props, filelike)
-
- return uid, vid
+
+ self._mpcache[uid] = mp
+ return uid, str(vid)
@dbus.service.method(DS_DBUS_INTERFACE,
@@ -223,7 +295,18 @@ class DataStore(dbus.service.Object):
histories of the same object.
"""
## XXX: review this when repo merge code is in place
- mp = self._resolveMountpoint(mountpoint)
+
+ # dbus cleanup
+ uid = str(uid)
+ vid = vid and str(vid) or None
+ mountpoint = mountpoint and str(mountpoint) or None
+ target = target and str(target) or None
+ dir = dir and str(dir) or None
+
+ mp = self._mountpointFor(uid)
+ if not mp:
+ raise KeyError("Object with %s uid not found in datastore" % uid)
+
if "versions" not in mp.capabilities:
content = mp.get(uid)
props = content.properties
@@ -310,7 +393,7 @@ class DataStore(dbus.service.Object):
for hit in res:
existing = d.get(hit.id)
if not existing or \
- existing.get_property('mtime') < hit.get_property('mtime'):
+ existing.get_property('vid') < hit.get_property('vid'):
# XXX: age/version check
d[hit.id] = hit
@@ -434,11 +517,11 @@ class DataStore(dbus.service.Object):
return (d, len(results))
- def get(self, uid, mountpoint=None):
+ def get(self, uid, rev=None, mountpoint=None):
mp = self._resolveMountpoint(mountpoint)
c = None
try:
- c = mp.get(uid)
+ c = mp.get(uid, rev)
if c: return c
except KeyError:
pass
@@ -446,27 +529,36 @@ class DataStore(dbus.service.Object):
if not c:
for mp in self.mountpoints.itervalues():
try:
- c = mp.get(uid)
+ if "versions" in mp.capabilities:
+ c = mp.get(uid, rev)
+ else:
+ c = mp.get(uid)
+
if c: break
except KeyError:
continue
return c
@dbus.service.method(DS_DBUS_INTERFACE,
- in_signature='s',
+ in_signature='sss',
out_signature='s')
- def get_filename(self, uid):
- content = self.get(uid)
+ def get_filename(self, uid, vid=None, mountpoint=None):
+ vid = vid and str(vid) or None
+ mountpoint = mountpoint and str(mountpoint) or None
+ content = self.get(uid, vid, mountpoint)
if content:
try: return content.filename
except AttributeError: pass
return ''
@dbus.service.method(DS_DBUS_INTERFACE,
- in_signature='s',
+ in_signature='sss',
out_signature='a{sv}')
- def get_properties(self, uid):
- content = self.get(uid)
+ def get_properties(self, uid, vid=None, mountpoint=None):
+ vid = vid and str(vid) or None
+ mountpoint = mountpoint and str(mountpoint) or None
+
+ content = self.get(uid, vid, mountpoint)
props = content.properties
props['mountpoint'] = content.backingstore.id
return props
@@ -510,7 +602,7 @@ class DataStore(dbus.service.Object):
in_signature='ss',
out_signature='')
def delete(self, uid, mountpoint=None):
- content = self.get(uid, mountpoint)
+ content = self.get(uid, mountpoint=mountpoint)
if content:
content.backingstore.delete(uid)
self.Deleted(uid)
@@ -520,8 +612,13 @@ class DataStore(dbus.service.Object):
@dbus.service.signal(DS_DBUS_INTERFACE, signature="s")
def Deleted(self, uid): pass
+
+ @dbus.service.method(DS_DBUS_INTERFACE,
+ in_signature='',
+ out_signature='')
def stop(self):
- """shutdown the service"""
+ """shutdown the service. this is intended only for automated
+ testing or system shutdown."""
self.Stopped()
self._connection.get_connection()._unregister_object_path(DS_OBJECT_PATH)
for mp in self.mountpoints.values(): mp.stop()