On Tue, 2011-10-25 at 09:24 +0200, Lukas Zeller wrote:
> On Oct 24, 2011, at 16:55 , Patrick Ohly wrote:
> 
> > Why is it necessary to read before trying to delete? If the item exists,
> > then reading it is a fairly expensive test.
> 
> Unfortunately, with some some backends, this is the only test that reliably 
> works. For example, some SQL databases (or their ODBC driver) can't return a 
> correct "number of affected rows" for DELETE statements. So reading before 
> deleting was the only way to detect if any of the subdatastores really had 
> that item and correctly return 404 or 200.
> 
> > So far, my backends were written with the expectation that they have to
> > cope with delete requests for items which are already deleted. This
> > follows from the inherent race condition between syncing and some other
> > process which might delete items while a sync runs.
> > 
> > Are all backends expected to return 404 when an item doesn't exist?
> 
> Not all backends (see above, built-in ODBC can't), but yes, for plugin
> backends, returning proper error codes is specified, also for delete.
> Still, if a plugin would not conform to that in its implementation of
> delete, that would probably have gone unnoticed so far.

I checked the API docs and did not see it mentioned explicitly.

In SyncEvolution I added some remarks about 404 (will probably not be
noticed) and also added automated Client::Source tests for it (which is
harder to ignore if someone runs the tests ;-)

I also changed the error logging so that 404 is reported to users in
ReadItem and not logged in DeleteItem (while still returned to the
engine).

> Of course, the test is a bit expensive - if that's a concern, one
> could differentiate between plugin datastores and others (all the
> builtin ones, including SQL/ODBC), and use the expensive read test
> only for the latter. Like a virtual dsDeleteDetectsItemPresence()
> which returns false by default, but can be overridden in plugin
> datastores to return true.

The patch below implements that idea (from the for-master/bmc23744
branch). SyncEvolution relies on that patch to avoid the (harmless)
ERROR messages for "item not found" in ReadItem(), although everything
should work okay also without the patch.

Does that look right?

>From fb5cc0dc19fb60e40a4ca2dcfe44a52a75ec7354 Mon Sep 17 00:00:00 2001
From: Patrick Ohly <patrick.o...@intel.com>
Date: Fri, 28 Oct 2011 15:42:57 +0200
Subject: [PATCH] server engine: more efficient deletion in superdatastore

Attempting to read an item just to check whether it exists is expensive.
It also may trigger error messages when the item does not exist (done by
SyncEvolution).

Therefore, if a data store is able to properly report whether it found
the item which is to be deleted, a different logic is used for
deletion in the superdatastore:
- attempt to delete until it succeeds in a subdatastore
- recreate the sync item from the copy after a failed delete attempt
  (which deletes the sync item), if there is another loop iteration

Deleting the copy of the sync item was moved to the function exit code
to avoid code duplication.

By default all data stores are assumed to not support 404 in its
delete operation. The only exception is the plugin API. The (somewhat
undocumented) assumption is that all methods properly report 404 when
the requested item does not exist. The "attempt to read" code already
relied on that. Now the code relies on that for the "attempt to
delete".

Probably it even works when the plugin returns some other error
code (the "regular" value will be false in that case) or no error code
at all: the translation code between remote ID and local ID will
already detect that the item is not in the mapping table if it is not
in the subdatastore. So the actual plugin will not get called at all
in the case where its expected behavior would matter.
---
 src/DB_interfaces/api_db/pluginapids.h |    4 ++
 src/sysync/superdatastore.cpp          |   80 ++++++++++++++++++++++----------
 src/sysync/syncdatastore.h             |    2 +
 3 files changed, 62 insertions(+), 24 deletions(-)

diff --git a/src/DB_interfaces/api_db/pluginapids.h 
b/src/DB_interfaces/api_db/pluginapids.h
index 4fbfc79..f215094 100755
--- a/src/DB_interfaces/api_db/pluginapids.h
+++ b/src/DB_interfaces/api_db/pluginapids.h
@@ -172,6 +172,10 @@ public:
   virtual void dsResetDataStore(void) { InternalResetDataStore(); 
inherited::dsResetDataStore(); };
   virtual ~TPluginApiDS();
 
+  // override TSyncDataStore: the plugin must be able to return 404
+  // when an item is not found during delete
+  virtual bool dsDeleteDetectsItemPresence() const { return true; }
+
   #ifndef BINFILE_ALWAYS_ACTIVE
   /// @name apiXXXX methods defining the interface from TCustomImplDS to 
TXXXApi actual API implementations
   /// @{
diff --git a/src/sysync/superdatastore.cpp b/src/sysync/superdatastore.cpp
index e70d7bb..5c0a2b5 100755
--- a/src/sysync/superdatastore.cpp
+++ b/src/sysync/superdatastore.cpp
@@ -530,7 +530,7 @@ bool TSuperDataStore::engProcessRemoteItem(
   bool regular=true;
   string datatext;
   #ifdef SYSYNC_SERVER
-  TSyncItem *itemcopyP;
+  TSyncItem *itemcopyP = NULL;
   #endif
 
   // show
@@ -617,42 +617,71 @@ bool TSuperDataStore::engProcessRemoteItem(
       case sop_delete:
       case sop_copy:
         // item has no local ID AND no data, only a remoteID:
-        // we must try to read item from all subdatastores by remoteID until
-        // one is found
+        // we must try to read item or attempt to delete in all subdatastores 
by remoteID
+        // until found in one of them
+
         // get an empty item of correct type to call logicRetrieveItemByID
         itemcopyP = 
getLocalReceiveType()->newSyncItem(getRemoteSendType(),this);
         // - only remote ID is relevant, leave everything else empty
         itemcopyP->setRemoteID(syncitemP->getRemoteID());
-        // try to read item from all subdatastores
+        // try to find item in any of the subdatastores
         for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
           linkP = &(*pos);
           // always start with 200
           aStatusCommand.setStatusCode(200);
-          // now try to read
-          PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,(
-            "Trying to read item by remoteID='%s' from subdatastore '%s' to 
see if it is there",
-            itemcopyP->getRemoteID(),
-            linkP->fDatastoreLinkP->getName()
-          ));
-          
regular=linkP->fDatastoreLinkP->logicRetrieveItemByID(*itemcopyP,aStatusCommand);
-          // must be ok AND not 404 (item not found)
-          if (regular && aStatusCommand.getStatusCode()!=404) {
-            PDEBUGPRINTFX(DBG_DATA,(
-              "Item found in subdatastore '%s', deleting it there",
+
+          // item deleted by a failed engProcessRemoteItem()?
+          if (!syncitemP) {
+            // recreate it for next attempt
+            syncitemP = 
getLocalReceiveType()->newSyncItem(getRemoteSendType(),this);
+            syncitemP->setRemoteID(itemcopyP->getRemoteID());
+            syncitemP->setSyncOp(sop);
+          }
+
+          if (linkP->fDatastoreLinkP->dsDeleteDetectsItemPresence()) {
+            // attempt to delete, consuming original item on success
+            
regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
+            // must be ok AND not 404 (item not found)
+            if (regular && aStatusCommand.getStatusCode()!=404) {
+              PDEBUGPRINTFX(DBG_DATA,(
+                "Item found in subdatastore '%s', deleted it there",
+                linkP->fDatastoreLinkP->getName()
+              ));
+
+              // done
+              goto done;
+            } else {
+              // syncitemP was deleted by engProcessRemoteItem() (for
+              // example, in TStdLogicDS::logicProcessRemoteItem()),
+              // so we must remember to recreate it from the copy for
+              // another attempt if there is one
+              syncitemP = NULL;
+            }
+          } else {
+            // try to read first to determine whether the subdatastore 
contains the item;
+            // necessary because the subdatastore is not able to report a 404 
error in
+            // the delete operation when the item does not exist
+            PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,(
+              "Trying to read item by remoteID='%s' from subdatastore '%s' to 
see if it is there",
+              itemcopyP->getRemoteID(),
               linkP->fDatastoreLinkP->getName()
             ));
-            // now we can delete or copy, consuming original item
-            
regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
-            // delete duplicated item as well
-            delete itemcopyP;
-            // done
-            regular=true;
-            goto done;
+            
regular=linkP->fDatastoreLinkP->logicRetrieveItemByID(*itemcopyP,aStatusCommand);
+            // must be ok AND not 404 (item not found)
+            if (regular && aStatusCommand.getStatusCode()!=404) {
+              PDEBUGPRINTFX(DBG_DATA,(
+                "Item found in subdatastore '%s', deleting it there",
+                linkP->fDatastoreLinkP->getName()
+              ));
+              // now we can delete or copy, consuming original item
+              
regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
+              // done
+              regular=true;
+              goto done;
+            }
           }
         }
         // none of the datastores could process this item --> error
-        // - delete duplicated item
-        delete itemcopyP;
         // - make sure delete reports 200 for incomplete-rollback-datastores
         if (aStatusCommand.getStatusCode()==404 && sop!=sop_copy) {
           // not finding an item for delete might be ok for remote...
@@ -722,6 +751,9 @@ nods:
   regular=false;
   goto done;
 done:
+  #ifdef SYSYNC_SERVER
+  delete itemcopyP;
+  #endif
   PDEBUGENDBLOCK("SuperProcessItem");
   return regular;
 } // TSuperDataStore::engProcessRemoteItem
diff --git a/src/sysync/syncdatastore.h b/src/sysync/syncdatastore.h
index 78e4559..0672341 100755
--- a/src/sysync/syncdatastore.h
+++ b/src/sysync/syncdatastore.h
@@ -56,6 +56,8 @@ public:
   virtual uInt16 isDatastore(const char *aDatastoreURI);
   // - returns if specified type is used by this datastore
   bool doesUseType(TSyncItemType *aSyncItemType, TTypeVariantDescriptor 
*aVariantDescP=NULL);
+  // - true if data store is able to return 404 when asked to delete 
non-existent item
+  virtual bool dsDeleteDetectsItemPresence() const { return false; }
   // - get common types to send to or receive from another datastore
   TSyncItemType *getTypesForTxTo(TSyncDataStore *aDatastoreP, TSyncItemType 
**aCorrespondingTypePP=NULL);
   TSyncItemType *getTypesForRxFrom(TSyncDataStore *aDatastoreP, TSyncItemType 
**aCorrespondingTypePP=NULL);
-- 
1.7.2.5




_______________________________________________
os-libsynthesis mailing list
os-libsynthesis@synthesis.ch
http://lists.synthesis.ch/mailman/listinfo/os-libsynthesis

Reply via email to