Commit 2d2c38f9 authored by Aurel's avatar Aurel

implement better deletion mecanism & remove workflow transition overhead

parent 1b4cd9c7
...@@ -78,7 +78,10 @@ class SyncMLAsynchronousEngine(EngineMixin): ...@@ -78,7 +78,10 @@ class SyncMLAsynchronousEngine(EngineMixin):
syncml_response = self._generateBaseResponse(subscription) syncml_response = self._generateBaseResponse(subscription)
syncml_response.addFinal() syncml_response.addFinal()
else: else:
self.runGetAndActivate(subscription=subscription, tag=tag) # Make sure it is launched after indexation step
self.runGetAndActivate(subscription=subscription, tag=tag,
after_method_id=("getAndIndex",
"SQLCatalog_indexSyncMLSignatureList"))
syncml_logger.info("X-> Client is sendind modification in activities") syncml_logger.info("X-> Client is sendind modification in activities")
# As we generated all activities to send data at once, process must not # As we generated all activities to send data at once, process must not
# go back here, go into processing state thus status will be applied and # go back here, go into processing state thus status will be applied and
...@@ -163,10 +166,10 @@ class SyncMLAsynchronousEngine(EngineMixin): ...@@ -163,10 +166,10 @@ class SyncMLAsynchronousEngine(EngineMixin):
# Apply command & send modifications # Apply command & send modifications
# Apply status about object send & synchronized if any # Apply status about object send & synchronized if any
sync_status_counter = self._readStatusList(syncml_request, subscriber, self._readStatusList(syncml_request, subscriber,
generate_alert=True) generate_alert=True)
syncml_response = None syncml_response = None
tag = subscription_path = subscriber.getRelativeUrl() tag = subscriber.getRelativeUrl()
after_method_id = None after_method_id = None
if subscriber.getSynchronizationState() == "sending_modifications": if subscriber.getSynchronizationState() == "sending_modifications":
if syncml_request.isFinal: if syncml_request.isFinal:
...@@ -202,15 +205,13 @@ class SyncMLAsynchronousEngine(EngineMixin): ...@@ -202,15 +205,13 @@ class SyncMLAsynchronousEngine(EngineMixin):
if syncml_request.isFinal: if syncml_request.isFinal:
# Server then sends its modifications # Server then sends its modifications
subscriber.sendModifications() subscriber.sendModifications()
# Now that everything is ok, init sync information # Run indexation only once client have sent its modifications
if subscriber.getSyncmlAlertCode() not in ("one_way_from_client", subscriber.indexSourceData()
"refresh_from_client_only"):
# Reset signature only if we have to check modifications on server side
subscriber.initialiseSynchronization()
# Start to send modification only once we have processed # Start to send modification only once we have processed
# all message from client # all message from client
after_method_id='processServerSynchronization', after_method_id=('processServerSynchronization',
'SQLCatalog_indexSyncMLDocumentList')
# XXX after tag might also be required to make sure all data are indexed
tag = (tag, "%s_reset" % subscriber.getPath(),) tag = (tag, "%s_reset" % subscriber.getPath(),)
# Do not continue in elif, as sending modifications is done in the same # Do not continue in elif, as sending modifications is done in the same
# package as sending notifications # package as sending notifications
...@@ -242,10 +243,9 @@ class SyncMLAsynchronousEngine(EngineMixin): ...@@ -242,10 +243,9 @@ class SyncMLAsynchronousEngine(EngineMixin):
after_tag=tag).sendMessage( after_tag=tag).sendMessage(
xml=str(syncml_response)) xml=str(syncml_response))
def runGetAndActivate(self, subscription, tag, after_method_id=None): def runGetAndActivate(self, subscription, tag, after_method_id=None):
""" """
Generate tag and method parameter and call the getAndActivate method Launch the browsing of GID that will call the generation of syncml commands
""" """
activate_kw = { activate_kw = {
'activity' : 'SQLQueue', 'activity' : 'SQLQueue',
...@@ -253,20 +253,16 @@ class SyncMLAsynchronousEngine(EngineMixin): ...@@ -253,20 +253,16 @@ class SyncMLAsynchronousEngine(EngineMixin):
'tag' :tag, 'tag' :tag,
'priority' :ACTIVITY_PRIORITY 'priority' :ACTIVITY_PRIORITY
} }
method_kw = {
'subscription_path' : subscription.getRelativeUrl(),
}
pref = getSite().portal_preferences pref = getSite().portal_preferences
subscription.getAndActivate( subscription.getAndActivate(
callback="sendSyncCommand", callback="sendSyncCommand",
method_kw=method_kw,
activate_kw=activate_kw, activate_kw=activate_kw,
packet_size=pref.getPreferredDocumentRetrievedPerActivityCount(), packet_size=pref.getPreferredDocumentRetrievedPerActivityCount(),
activity_count=pref.getPreferredRetrievalActivityCount(), activity_count=pref.getPreferredRetrievalActivityCount(),
) )
# Then get deleted document # then send the final message of this sync part
# this will send also the final message of this sync part subscription.activate(after_tag=tag,
subscription.activate(after_tag=tag)._getDeletedData() priority=ACTIVITY_PRIORITY+1)._sendFinalMessage()
return True return True
......
...@@ -145,9 +145,10 @@ class EngineMixin(object): ...@@ -145,9 +145,10 @@ class EngineMixin(object):
signature = domain.getSignatureFromGid(object_gid) signature = domain.getSignatureFromGid(object_gid)
if status['status_code'] == resolveSyncmlStatusCode('success'): if status['status_code'] == resolveSyncmlStatusCode('success'):
if signature: if signature:
domain.z_delete_data_from_path(path=signature.getPath())
domain._delObject(signature.getId()) domain._delObject(signature.getId())
else: else:
raise ValueError("Found no signature to delete") raise ValueError("Found no signature to delete for gid %s" %(object_gid,))
else: else:
raise ValueError("Unknown status code : %r" % (status['status_code'],)) raise ValueError("Unknown status code : %r" % (status['status_code'],))
syncml_logger.error("\tObject deleted %s" % syncml_logger.error("\tObject deleted %s" %
...@@ -191,10 +192,8 @@ class EngineMixin(object): ...@@ -191,10 +192,8 @@ class EngineMixin(object):
if subscription.getAuthenticationState() != 'logged_in': if subscription.getAuthenticationState() != 'logged_in':
# Workflow action # Workflow action
subscription.login() subscription.login()
if subscription.getSyncmlAlertCode() not in ("one_way_from_server",
"refresh_from_server_only"): subscription.indexSourceData(client=True)
# Reset signature only if client send its modification to server
subscription.initialiseSynchronization()
# Create the package 1 # Create the package 1
syncml_response = SyncMLResponse() syncml_response = SyncMLResponse()
...@@ -301,7 +300,9 @@ class EngineMixin(object): ...@@ -301,7 +300,9 @@ class EngineMixin(object):
'one_way_from_server', 'one_way_from_server',
'refresh_from_client_only', 'refresh_from_client_only',
'one_way_from_client'): 'one_way_from_client'):
# XXX Why re-editing here ? # Make sure we update configuration based on publication data
# so that manual edition is propagated
# XXX Must check all properties that must be setted
subscriber.setXmlBindingGeneratorMethodId( subscriber.setXmlBindingGeneratorMethodId(
publication.getXmlBindingGeneratorMethodId()) publication.getXmlBindingGeneratorMethodId())
subscriber.setConduitModuleId(publication.getConduitModuleId()) subscriber.setConduitModuleId(publication.getConduitModuleId())
......
...@@ -29,6 +29,7 @@ from logging import getLogger ...@@ -29,6 +29,7 @@ from logging import getLogger
from Products.ERP5SyncML.Engine.EngineMixin import EngineMixin from Products.ERP5SyncML.Engine.EngineMixin import EngineMixin
from Products.ERP5SyncML.SyncMLConstant import SynchronizationError from Products.ERP5SyncML.SyncMLConstant import SynchronizationError
from Products.ERP5.ERP5Site import getSite
syncml_logger = getLogger('ERP5SyncML') syncml_logger = getLogger('ERP5SyncML')
...@@ -81,13 +82,12 @@ class SyncMLSynchronousEngine(EngineMixin): ...@@ -81,13 +82,12 @@ class SyncMLSynchronousEngine(EngineMixin):
# We only get data from server # We only get data from server
finished = True finished = True
else: else:
finished = subscription._getSyncMLData( finished = subscription._getSyncMLData(syncml_response=syncml_response,
syncml_response=syncml_response, min_gid=None, max_gid=None)
)
syncml_logger.info("-> Client sendind modification, finished %s" % (finished,)) syncml_logger.info("-> Client sendind modification, finished %s" % (finished,))
if finished: if finished:
# Add deleted objets # Add deleted objets
subscription._getDeletedData(syncml_response=syncml_response) #subscription._getDeletedData(syncml_response=syncml_response)
# Notify that all modifications were sent # Notify that all modifications were sent
syncml_response.addFinal() syncml_response.addFinal()
# Will then start processing sync commands from server # Will then start processing sync commands from server
...@@ -191,10 +191,8 @@ class SyncMLSynchronousEngine(EngineMixin): ...@@ -191,10 +191,8 @@ class SyncMLSynchronousEngine(EngineMixin):
if syncml_request.isFinal: if syncml_request.isFinal:
# Server will now send its modifications # Server will now send its modifications
subscriber.sendModifications() subscriber.sendModifications()
if subscriber.getSyncmlAlertCode() not in ("one_way_from_client", # Run indexation only once client has sent its modifications
"refresh_from_client_only"): subscriber.indexSourceData()
# Reset signature only if we have to check modifications on server side
subscriber.initialiseSynchronization()
# Do not continue in elif, as sending modifications is done in the same # Do not continue in elif, as sending modifications is done in the same
# package as sending notifications # package as sending notifications
...@@ -205,11 +203,11 @@ class SyncMLSynchronousEngine(EngineMixin): ...@@ -205,11 +203,11 @@ class SyncMLSynchronousEngine(EngineMixin):
# We only get data from client # We only get data from client
finished = True finished = True
else: else:
finished = subscriber._getSyncMLData( finished = subscriber._getSyncMLData(syncml_response=syncml_response,
syncml_response=syncml_response) min_gid=None, max_gid=None)
syncml_logger.info("-> Server sendind data, finished %s" % (finished,)) syncml_logger.info("-> Server sendind data, finished %s" % (finished,))
if finished: if finished:
subscriber._getDeletedData(syncml_response=syncml_response) #subscriber._getDeletedData(syncml_response=syncml_response)
syncml_response.addFinal() syncml_response.addFinal()
subscriber.waitNotifications() subscriber.waitNotifications()
# Do not go into finished here as we must wait for # Do not go into finished here as we must wait for
......
...@@ -399,13 +399,14 @@ class SynchronizationTool(BaseTool): ...@@ -399,13 +399,14 @@ class SynchronizationTool(BaseTool):
return str(syncml_response) return str(syncml_response)
# As engines are not zodb objects, the tool acts as a placeholder for methods
# that need to be called in activities
def applySyncCommand(self, subscription_path, response_message_id, def applySyncCommand(self, subscription_path, response_message_id,
activate_kw, **kw): activate_kw, **kw):
""" """
This methods is intented to be called by asynchronous engine in activity to This methods is intented to be called by asynchronous engine in activity to
apply sync commands for a subset of data apply sync commands for a subset of data
As engines are not zodb object, the tool acts as a placeholder for method
that need to be called in activities
""" """
subscription = self.restrictedTraverse(subscription_path) subscription = self.restrictedTraverse(subscription_path)
assert subscription is not None, "Impossible to find subscription %s" \ assert subscription is not None, "Impossible to find subscription %s" \
...@@ -437,13 +438,11 @@ class SynchronizationTool(BaseTool): ...@@ -437,13 +438,11 @@ class SynchronizationTool(BaseTool):
def sendSyncCommand(self, id_list, message_id, subscription_path, def sendSyncCommand(self, gid_list, message_id, subscription_path,
activate_kw, is_final_message=False): activate_kw, first_call=False, last_call=False):
""" """
This methods is intented to be called by asynchronous engine in activity to This methods is intented to be called by asynchronous engine in activity to
send sync commands for a subset of data send sync commands for a subset of data
As engines are not zodb object, the tool acts as a placeholder for method
that need to be called in activities
""" """
subscription = self.restrictedTraverse(subscription_path) subscription = self.restrictedTraverse(subscription_path)
assert subscription is not None, "Impossible to find subscription %s" \ assert subscription is not None, "Impossible to find subscription %s" \
...@@ -457,17 +456,14 @@ class SynchronizationTool(BaseTool): ...@@ -457,17 +456,14 @@ class SynchronizationTool(BaseTool):
source=subscription.getSubscriptionUrlString()) source=subscription.getSubscriptionUrlString())
syncml_response.addBody() syncml_response.addBody()
subscription._getSyncMLData( subscription._getSyncMLData(
syncml_response=syncml_response, syncml_response=syncml_response,
id_list=id_list, gid_list=gid_list,
first_call=first_call,
last_call=last_call,
) )
if is_final_message: # Send the message in activity to prevent recomputation of data in case of
# Notify that all modifications were sent
syncml_response.addFinal()
# Send the message in activity to prevent recomputing data in case of
# transport failure # transport failure
# activate_kw["group_method_id"] = None # activate_kw["group_method_id"] = None
# activate_kw["group_method_cost"] = .05 # activate_kw["group_method_cost"] = .05
......
...@@ -44,6 +44,7 @@ from Products.ERP5SyncML.Document import SyncMLSubscription ...@@ -44,6 +44,7 @@ from Products.ERP5SyncML.Document import SyncMLSubscription
from Products.ERP5SyncML.tests.testERP5SyncMLMixin import TestERP5SyncMLMixin \ from Products.ERP5SyncML.tests.testERP5SyncMLMixin import TestERP5SyncMLMixin \
as TestMixin as TestMixin
from Products.ERP5Type.tests.backportUnittest import expectedFailure from Products.ERP5Type.tests.backportUnittest import expectedFailure
from _mysql_exceptions import OperationalError
class TestERP5SyncMLMixin(TestMixin): class TestERP5SyncMLMixin(TestMixin):
...@@ -98,6 +99,8 @@ class TestERP5SyncMLMixin(TestMixin): ...@@ -98,6 +99,8 @@ class TestERP5SyncMLMixin(TestMixin):
def afterSetUp(self): def afterSetUp(self):
"""Setup.""" """Setup."""
self.login() self.login()
self.portal.z_drop_syncml()
self.portal.z_create_syncml()
# This test creates Person inside Person, so we modifiy type information to # This test creates Person inside Person, so we modifiy type information to
# allow anything inside Person (we'll cleanup on teardown) # allow anything inside Person (we'll cleanup on teardown)
self.getTypesTool().getTypeInfo('Person').filter_content_types = 0 self.getTypesTool().getTypeInfo('Person').filter_content_types = 0
...@@ -228,6 +231,7 @@ class TestERP5SyncMLMixin(TestMixin): ...@@ -228,6 +231,7 @@ class TestERP5SyncMLMixin(TestMixin):
result = portal_sync.processClientSynchronization(subscription.getPath()) result = portal_sync.processClientSynchronization(subscription.getPath())
self.tic() self.tic()
nb_message += 1 nb_message += 1
self.tic()
return nb_message return nb_message
def synchronizeWithBrokenMessage(self, id): def synchronizeWithBrokenMessage(self, id):
...@@ -948,6 +952,8 @@ return [context[%r]] ...@@ -948,6 +952,8 @@ return [context[%r]]
person_server.manage_delObjects(self.id1) person_server.manage_delObjects(self.id1)
person_client1 = self.getPersonClient1() person_client1 = self.getPersonClient1()
person_client1.manage_delObjects(self.id2) person_client1.manage_delObjects(self.id2)
# import ipdb
# ipdb.set_trace()
self.synchronize(self.sub_id1) self.synchronize(self.sub_id1)
self.synchronize(self.sub_id2) self.synchronize(self.sub_id2)
self.checkSynchronizationStateIsSynchronized() self.checkSynchronizationStateIsSynchronized()
...@@ -1543,7 +1549,7 @@ return [context[%r]] ...@@ -1543,7 +1549,7 @@ return [context[%r]]
self.assertEquals(client_person.getLastName(), self.last_name1) self.assertEquals(client_person.getLastName(), self.last_name1)
# reset for refresh sync # reset for refresh sync
# after synchronize, the client object retrieve value of server # after synchronization, the client retrieves value from server
self.resetSignaturePublicationAndSubscription() self.resetSignaturePublicationAndSubscription()
self.synchronize(self.sub_id1) self.synchronize(self.sub_id1)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment