소스 검색

Implement better IMR cache and cleanup methods; immediate access to
IMR after create or update.

Stefano Cossu 5 년 전
부모
커밋
b1a957acf3

+ 11 - 6
lakesuperior/api/resource.py

@@ -203,8 +203,10 @@ def create(parent, slug=None, **kwargs):
       :py:meth:`~lakesuperior.model.ldp_factory.LdpFactory.from_provided`
       method.
 
-    :rtype: str
-    :return: UID of the new resource.
+    :rtype: tuple(str, lakesuperior.model.ldpr.Ldpr)
+    :return: A tuple of:
+        1. Event type (str): whether the resource was created or updated.
+        2. Resource (lakesuperior.model.ldpr.Ldpr): The new or updated resource.
     """
     uid = LdpFactory.mint_uid(parent, slug)
     logger.debug('Minted UID for new resource: {}'.format(uid))
@@ -212,7 +214,7 @@ def create(parent, slug=None, **kwargs):
 
     rsrc.create_or_replace(create_only=True)
 
-    return uid
+    return rsrc
 
 
 @transaction(True)
@@ -225,10 +227,13 @@ def create_or_replace(uid, **kwargs):
         :py:meth:`~lakesuperior.model.ldp_factory.LdpFactory.from_provided`
         method.
 
-    :rtype: str
-    :return: Event type: whether the resource was created or updated.
+    :rtype: tuple(str, lakesuperior.model.ldpr.Ldpr)
+    :return: A tuple of:
+        1. Event type (str): whether the resource was created or updated.
+        2. Resource (lakesuperior.model.ldpr.Ldpr): The new or updated resource.
     """
-    return LdpFactory.from_provided(uid, **kwargs).create_or_replace()
+    rsrc = LdpFactory.from_provided(uid, **kwargs)
+    return rsrc.create_or_replace(), rsrc
 
 
 @transaction(True)

+ 2 - 3
lakesuperior/dictionaries/namespaces.py

@@ -29,9 +29,8 @@ core_namespaces = {
     'xsd' : rdflib.namespace.XSD,
 }
 
-ns_collection = core_namespaces.copy()
-custom_ns = {pfx: Namespace(ns) for pfx, ns in config['namespaces'].items()}
-ns_collection.update(custom_ns)
+ns_collection  = {pfx: Namespace(ns) for pfx, ns in config['namespaces'].items()}
+ns_collection.update(core_namespaces)
 
 ns_mgr = NamespaceManager(Graph())
 

+ 3 - 3
lakesuperior/endpoints/ldp.py

@@ -250,7 +250,7 @@ def post_resource(parent_uid):
         rdf_data = rdf_fmt = None
 
     try:
-        uid = rsrc_api.create(
+        rsrc = rsrc_api.create(
             parent_uid, slug, stream=stream, mimetype=mimetype,
             rdf_data=rdf_data, rdf_fmt=rdf_fmt, handling=handling,
             disposition=disposition)
@@ -263,8 +263,8 @@ def post_resource(parent_uid):
     except ServerManagedTermError as e:
         return str(e), 412
 
-    uri = g.tbox.uid_to_uri(uid)
     hdr = {'Location' : uri}
+    uri = g.tbox.uid_to_uri(rsrc.uid)
 
     if mimetype and rdf_fmt is None:
         hdr['Link'] = '<{0}/fcr:metadata>; rel="describedby"; anchor="{0}"'\
@@ -302,7 +302,7 @@ def put_resource(uid):
         rdf_data = rdf_fmt = None
 
     try:
-        evt = rsrc_api.create_or_replace(
+        evt, rsrc = rsrc_api.create_or_replace(
             uid, stream=stream, mimetype=mimetype,
             rdf_data=rdf_data, rdf_fmt=rdf_fmt, handling=handling,
             disposition=disposition)

+ 35 - 11
lakesuperior/model/ldpr.py

@@ -130,6 +130,19 @@ class Ldpr(metaclass=ABCMeta):
     }
     """RDF types that don't get versioned."""
 
+    _cached_attrs = (
+        '_imr',
+        '_metadata',
+        '_version_info',
+        '_is_stored',
+        '_types',
+        '_ldp_types',
+    )
+    """
+    Attributes cached from the store.
+
+    These are used by setters and can be cleared with :py:meth:`_clear_cache`.
+    """
 
     ## MAGIC METHODS ##
 
@@ -142,7 +155,7 @@ class Ldpr(metaclass=ABCMeta):
         in which case it will be converted.
         :param dict repr_opts: Options used to retrieve the IMR. See
         `parse_rfc7240` for format details.
-        :param str provd_rdf: RDF data provided by the client in
+        :param str provided_imr: RDF data provided by the client in
         operations such as `PUT` or `POST`, serialized as a string. This sets
         the `provided_imr` property.
         """
@@ -388,6 +401,9 @@ class Ldpr(metaclass=ABCMeta):
         identical, are wrappers for this method.
 
         :param boolean create_only: Whether this is a create-only operation.
+        :rtype: str
+
+        :return: Event type: whether the resource was created or updated.
         """
         create = create_only or not self.is_stored
 
@@ -408,7 +424,7 @@ class Ldpr(metaclass=ABCMeta):
 
         self.modify(ev_type, remove_trp, add_trp)
 
-        self.imr = add_trp
+        #self.imr = add_trp
 
         return ev_type
 
@@ -699,21 +715,29 @@ class Ldpr(metaclass=ABCMeta):
         """
         rdfly.modify_rsrc(self.uid, remove_trp, add_trp)
 
-        # Reset IMR buffer.
-        if hasattr(self, '_imr'):
-            delattr(self, '_imr')
-            try:
-                self.imr
-            except (ResourceNotExistsError, TombstoneError):
-                pass
+        self._clear_cache()
 
         if (
                 ev_type is not None and
                 env.app_globals.config['application'].get('messaging')):
-            logger.debug('Enqueuing message for {}'.format(self.uid))
+            logger.debug(f'Enqueuing message for {self.uid}')
             self._enqueue_msg(ev_type, remove_trp, add_trp)
 
 
+    def _clear_cache(self):
+        """
+        Clear stale model attributes.
+
+        This method removes class members populated with data pulled from the
+        store that may have become stale after a resource update.
+        """
+        for attr in self._cached_attrs:
+            try:
+                delattr(self, attr)
+            except AttributeError:
+                pass
+
+
     def _enqueue_msg(self, ev_type, remove_trp=None, add_trp=None):
         """
         Compose a message about a resource change.
@@ -727,7 +751,7 @@ class Ldpr(metaclass=ABCMeta):
             rsrc_type = tuple(str(t) for t in self.types)
             actor = self.metadata.value(nsc['fcrepo'].createdBy)
         except (ResourceNotExistsError, TombstoneError):
-            rsrc_type = ()
+            rsrc_type = set()
             actor = None
             for t in add_trp:
                 if t[1] == RDF.type:

+ 5 - 4
lakesuperior/store/ldp_rs/lmdb_triplestore.pyx

@@ -316,11 +316,12 @@ cdef class SimpleGraph:
             unsigned char spok[TRP_KLEN]
 
         self.data = set()
-        keyset = store.triple_keys(*lookup)
+        with store.txn_ctx():
+            keyset = store.triple_keys(*lookup)
+            for i in range(keyset.ct):
+                spok = keyset.data + i * TRP_KLEN
+                self.data.add(store.from_trp_key(spok[: TRP_KLEN]))
 
-        for i in range(keyset.ct):
-            spok = keyset.data + i * TRP_KLEN
-            self.data.add(store.from_trp_key(spok[: TRP_KLEN]))
 
     # Basic set operations.
 

+ 7 - 10
tests/1_api/test_resource_api.py

@@ -105,7 +105,7 @@ class TestResourceCRUD:
         gr = Graph().parse(
             data='<> a <http://ex.org/type#A> .', format='turtle',
             publicID=uri)
-        evt = rsrc_api.create_or_replace(uid, graph=gr)
+        evt, _ = rsrc_api.create_or_replace(uid, graph=gr)
 
         rsrc = rsrc_api.get(uid)
         assert rsrc.imr[
@@ -133,7 +133,7 @@ class TestResourceCRUD:
         gr1 = Graph().parse(
             data='<> a <http://ex.org/type#A> .', format='turtle',
             publicID=uri)
-        evt = rsrc_api.create_or_replace(uid, graph=gr1)
+        evt, _ = rsrc_api.create_or_replace(uid, graph=gr1)
         assert evt == RES_CREATED
 
         rsrc = rsrc_api.get(uid)
@@ -146,7 +146,7 @@ class TestResourceCRUD:
             data='<> a <http://ex.org/type#B> .', format='turtle',
             publicID=uri)
         #pdb.set_trace()
-        evt = rsrc_api.create_or_replace(uid, graph=gr2)
+        evt, _ = rsrc_api.create_or_replace(uid, graph=gr2)
         assert evt == RES_UPDATED
 
         rsrc = rsrc_api.get(uid)
@@ -292,10 +292,9 @@ class TestResourceCRUD:
         Create an LDP Direct Container via POST.
         """
         rsrc_api.create_or_replace('/member')
-        dc_uid = rsrc_api.create(
+        dc_rsrc = rsrc_api.create(
                 '/', 'test_dc_post', rdf_data=dc_rdf, rdf_fmt='turtle')
 
-        dc_rsrc = rsrc_api.get(dc_uid)
         member_rsrc = rsrc_api.get('/member')
 
         assert nsc['ldp'].Container in dc_rsrc.ldp_types
@@ -307,10 +306,9 @@ class TestResourceCRUD:
         Create an LDP Direct Container via PUT.
         """
         dc_uid = '/test_dc_put01'
-        rsrc_api.create_or_replace(
+        _, dc_rsrc = rsrc_api.create_or_replace(
                 dc_uid, rdf_data=dc_rdf, rdf_fmt='turtle')
 
-        dc_rsrc = rsrc_api.get(dc_uid)
         member_rsrc = rsrc_api.get('/member')
 
         assert nsc['ldp'].Container in dc_rsrc.ldp_types
@@ -322,11 +320,10 @@ class TestResourceCRUD:
         Add members to a direct container and verify special properties.
         """
         dc_uid = '/test_dc_put02'
-        rsrc_api.create_or_replace(
+        _, dc_rsrc = rsrc_api.create_or_replace(
                 dc_uid, rdf_data=dc_rdf, rdf_fmt='turtle')
 
-        dc_rsrc = rsrc_api.get(dc_uid)
-        child_uid = rsrc_api.create(dc_uid, None)
+        child_uid = rsrc_api.create(dc_uid, None).uid
         member_rsrc = rsrc_api.get('/member')
 
         assert member_rsrc.imr[