Commit 1c300e5c authored by Yoshinori Okuji's avatar Yoshinori Okuji

This big change optimizes the scheduling of active objects,

and fix some bugs.

The basic idea is to track a dependency graph to find executable
messages quickly. This makes the activity system far more efficient,
when you have many inter-dependent messages queued in the tables.

Also, this obsoletes the time shifting in the schedulers,
as executable messages can be found in a more efficient manner.
So the activity parameter "at_date" should work expectedly.

Now the API of validate methods in Activities return a
list of message objects instead of a boolean value. Such
a list contains messages that are depended upon by a given
message.

The validate method in Message accepts a new optional
parameter, check_order_validation, to indicate whether
order validation should be performed. The default behavior
has not changed.

getDependentMessageList is added to ActivityTool, Queue
and Message. This method collects dependent message for
a given message from all activities.

There are some other subtle changes. Look at the diffs for
more details.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@14039 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent df5ccf5a
...@@ -26,12 +26,17 @@ ...@@ -26,12 +26,17 @@
# #
############################################################################## ##############################################################################
import pickle, sys import cPickle, sys
from Acquisition import aq_base
from DateTime import DateTime from DateTime import DateTime
from Products.CMFActivity.ActivityTool import Message from zLOG import LOG, WARNING, ERROR
from zLOG import LOG
from ZODB.POSException import ConflictError from ZODB.POSException import ConflictError
import sha
from cStringIO import StringIO
try:
from transaction import get as get_transaction
except ImportError:
pass
# Error values for message validation # Error values for message validation
EXCEPTION = -1 EXCEPTION = -1
...@@ -93,14 +98,14 @@ class Queue: ...@@ -93,14 +98,14 @@ class Queue:
def queueMessage(self, activity_tool, m): def queueMessage(self, activity_tool, m):
activity_tool.deferredQueueMessage(self, m) activity_tool.deferredQueueMessage(self, m)
def deleteMessage(self, activity_tool, m): def deleteMessage(self, activity_tool, m):
if not getattr(m, 'is_deleted', 0): if not getattr(m, 'is_deleted', 0):
# We try not to delete twice # We try not to delete twice
# However this can not be garanteed in the case of messages loaded from SQL # However this can not be garanteed in the case of messages loaded from SQL
activity_tool.deferredDeleteMessage(self, m) activity_tool.deferredDeleteMessage(self, m)
m.is_deleted = 1 m.is_deleted = 1
def dequeueMessage(self, activity_tool, processing_node): def dequeueMessage(self, activity_tool, processing_node):
pass pass
...@@ -122,45 +127,102 @@ class Queue: ...@@ -122,45 +127,102 @@ class Queue:
self.is_awake[processing_node] = 0 self.is_awake[processing_node] = 0
self.is_alive[processing_node] = 0 self.is_alive[processing_node] = 0
def validate(self, activity_tool, message, **kw): def validate(self, activity_tool, message, check_order_validation=1, **kw):
""" """
This is the place where activity semantics is implemented This is the place where activity semantics is implemented
**kw contains all parameters which allow to implement synchronisation, **kw contains all parameters which allow to implement synchronisation,
constraints, delays, etc. constraints, delays, etc.
Standard synchronisation parameters: Standard synchronisation parameters:
after_method_id -- never validate message if after_method_id after_method_id -- never validate message if after_method_id
is in the list of methods which are is in the list of methods which are
going to be executed going to be executed
after_message_uid -- never validate message if after_message_uid after_message_uid -- never validate message if after_message_uid
is in the list of messages which are is in the list of messages which are
going to be executed going to be executed
after_path -- never validate message if after_path after_path -- never validate message if after_path
is in the list of path which are is in the list of path which are
going to be executed going to be executed
""" """
try: try:
if activity_tool.unrestrictedTraverse(message.object_path, None) is None: if activity_tool.unrestrictedTraverse(message.object_path, None) is None:
# Do not try to call methods on objects which do not exist # Do not try to call methods on objects which do not exist
LOG('WARNING ActivityTool', 0, LOG('CMFActivity', WARNING,
'Object %s does not exist' % '/'.join(message.object_path)) 'Object %s does not exist' % '/'.join(message.object_path))
return INVALID_PATH return INVALID_PATH
for k, v in kw.items(): if check_order_validation:
if activity_tool.validateOrder(message, k, v): for k, v in kw.iteritems():
return INVALID_ORDER if activity_tool.validateOrder(message, k, v):
return INVALID_ORDER
except ConflictError: except ConflictError:
raise raise
except: except:
LOG('WARNING ActivityTool', 0, LOG('CMFActivity', WARNING,
'Validation of Object %s raised exception' % '/'.join(message.object_path), 'Validation of Object %s raised exception' % '/'.join(message.object_path),
error=sys.exc_info()) error=sys.exc_info())
# Do not try to call methods on objects which cause errors # Do not try to call methods on objects which cause errors
return EXCEPTION return EXCEPTION
return VALID return VALID
def getDependentMessageList(self, activity_tool, message, **kw):
message_list = []
for k, v in kw.iteritems():
result = activity_tool.getDependentMessageList(message, k, v)
if result:
message_list.extend(result)
return message_list
def getExecutableMessageList(self, activity_tool, message, message_dict,
validation_text_dict):
"""Get messages which have no dependent message, and store them in the dictionary.
If the passed message itself is executable, simply store only that message.
Otherwise, try to find at least one message executable from dependent messages.
This may result in no new message, if all dependent messages are already present
in the dictionary, if all dependent messages are in different activities, or if
the message has a circular dependency.
The validation text dictionary is used only to cache the results of validations,
in order to reduce the number of SQL queries.
"""
if message.uid in message_dict:
# Nothing to do. But detect a circular dependency.
if message_dict[message.uid] is None:
LOG('CMFActivity', ERROR,
'message uid %r has a circular dependency' % (message.uid,))
return
cached_result = validation_text_dict.get(message.order_validation_text)
if cached_result is None:
message_list = message.getDependentMessageList(self, activity_tool)
get_transaction().commit() # Release locks.
if message_list:
# The result is not empty, so this message is not executable.
validation_text_dict[message.order_validation_text] = 0
now_date = DateTime()
for activity, m in message_list:
# Note that the messages may contain ones which are already assigned or not
# executable yet.
if activity is self and m.processing_node == -1 and m.date <= now_date:
# Call recursively. Set None as a marker to detect a circular dependency.
message_dict[message.uid] = None
try:
self.getExecutableMessageList(activity_tool, m, message_dict,
validation_text_dict)
finally:
del message_dict[message.uid]
else:
validation_text_dict[message.order_validation_text] = 1
message_dict[message.uid] = message
elif cached_result:
message_dict[message.uid] = message
else:
pass
def isAwake(self, activity_tool, processing_node): def isAwake(self, activity_tool, processing_node):
return self.is_awake[processing_node] return self.is_awake[processing_node]
...@@ -179,22 +241,40 @@ class Queue: ...@@ -179,22 +241,40 @@ class Queue:
pass pass
def loadMessage(self, s, **kw): def loadMessage(self, s, **kw):
m = pickle.loads(s) m = cPickle.load(StringIO(s))
m.__dict__.update(kw) m.__dict__.update(kw)
return m return m
def dumpMessage(self, m): def dumpMessage(self, m):
return pickle.dumps(m) return cPickle.dumps(m)
def getOrderValidationText(self, message):
# Return an identifier of validators related to ordering.
order_validation_item_list = []
key_list = message.activity_kw.keys()
key_list.sort()
for key in key_list:
method_id = "_validate_%s" % key
if hasattr(self, method_id):
order_validation_item_list.append((key, message.activity_kw[key]))
if len(order_validation_item_list) == 0:
# When no order validation argument is specified, skip the computation
# of the checksum for speed. Here, 'none' is used, because this never be
# identical to SHA1 hexdigest (which is always 40 characters), and 'none'
# is true in Python. This is important, because dtml-if assumes that an empty
# string is false, so we must use a non-empty string for this.
return 'none'
return sha.new(repr(order_validation_item_list)).hexdigest()
def getMessageList(self, activity_tool, processing_node=None,**kw): def getMessageList(self, activity_tool, processing_node=None,**kw):
return [] return []
def countMessage(self, activity_tool,**kw): def countMessage(self, activity_tool,**kw):
return 0 return 0
def countMessageWithTag(self, activity_tool,value): def countMessageWithTag(self, activity_tool,value):
return 0 return 0
# Transaction Management # Transaction Management
def prepareQueueMessage(self, activity_tool, m): def prepareQueueMessage(self, activity_tool, m):
# Called to prepare transaction commit for queued messages # Called to prepare transaction commit for queued messages
......
...@@ -27,8 +27,8 @@ ...@@ -27,8 +27,8 @@
############################################################################## ##############################################################################
from Products.CMFActivity.ActivityTool import registerActivity from Products.CMFActivity.ActivityTool import registerActivity
from Products.CMFActivity.Errors import ActivityFlushError
from Queue import Queue, VALID from Queue import Queue, VALID
from Products.CMFActivity.ActiveObject import DISTRIBUTABLE_STATE, INVOKE_ERROR_STATE, VALIDATE_ERROR_STATE
from zLOG import LOG from zLOG import LOG
......
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
from Products.CMFActivity.ActivityTool import registerActivity from Products.CMFActivity.ActivityTool import registerActivity
from Queue import Queue, VALID from Queue import Queue, VALID
from Products.CMFActivity.ActiveObject import DISTRIBUTABLE_STATE, INVOKE_ERROR_STATE, VALIDATE_ERROR_STATE
try: try:
from transaction import get as get_transaction from transaction import get as get_transaction
......
This diff is collapsed.
This diff is collapsed.
...@@ -86,14 +86,14 @@ def registerActivity(activity): ...@@ -86,14 +86,14 @@ def registerActivity(activity):
class Message: class Message:
"""Activity Message Class. """Activity Message Class.
Message instances are stored in an activity queue, inside the Activity Tool. Message instances are stored in an activity queue, inside the Activity Tool.
""" """
def __init__(self, object, active_process, activity_kw, method_id, args, kw): def __init__(self, obj, active_process, activity_kw, method_id, args, kw):
if type(object) is StringType: if isinstance(obj, str):
self.object_path = object.split('/') self.object_path = obj.split('/')
else: else:
self.object_path = object.getPhysicalPath() self.object_path = obj.getPhysicalPath()
if type(active_process) is StringType: if type(active_process) is StringType:
self.active_process = active_process.split('/') self.active_process = active_process.split('/')
elif active_process is None: elif active_process is None:
...@@ -114,7 +114,7 @@ class Message: ...@@ -114,7 +114,7 @@ class Message:
def getObject(self, activity_tool): def getObject(self, activity_tool):
"""return the object referenced in this message.""" """return the object referenced in this message."""
return activity_tool.unrestrictedTraverse(self.object_path) return activity_tool.unrestrictedTraverse(self.object_path)
def getObjectList(self, activity_tool): def getObjectList(self, activity_tool):
"""return the list of object that can be expanded from this message.""" """return the list of object that can be expanded from this message."""
try: try:
...@@ -124,9 +124,9 @@ class Message: ...@@ -124,9 +124,9 @@ class Message:
object_list = getattr(obj, expand_method_id)() object_list = getattr(obj, expand_method_id)()
except KeyError: except KeyError:
object_list = [self.getObject(activity_tool)] object_list = [self.getObject(activity_tool)]
return object_list return object_list
def hasExpandMethod(self): def hasExpandMethod(self):
"""return true if the message has an expand method. """return true if the message has an expand method.
An expand method is used to expand the list of objects and to turn a An expand method is used to expand the list of objects and to turn a
...@@ -134,7 +134,7 @@ class Message: ...@@ -134,7 +134,7 @@ class Message:
transactions affecting only one object at a time (this can prevent transactions affecting only one object at a time (this can prevent
duplicated method calls).""" duplicated method calls)."""
return self.activity_kw.has_key('expand_method_id') return self.activity_kw.has_key('expand_method_id')
def changeUser(self, user_name, activity_tool): def changeUser(self, user_name, activity_tool):
"""restore the security context for the calling user.""" """restore the security context for the calling user."""
uf = activity_tool.getPortalObject().acl_users uf = activity_tool.getPortalObject().acl_users
...@@ -169,7 +169,7 @@ class Message: ...@@ -169,7 +169,7 @@ class Message:
ActiveResult(object_path=object, ActiveResult(object_path=object,
method_id=self.method_id, method_id=self.method_id,
result=result)) # XXX Allow other method_id in future result=result)) # XXX Allow other method_id in future
def __call__(self, activity_tool): def __call__(self, activity_tool):
try: try:
obj = self.getObject(activity_tool) obj = self.getObject(activity_tool)
...@@ -198,8 +198,13 @@ class Message: ...@@ -198,8 +198,13 @@ class Message:
if hasattr(activity_tool, 'error_log'): if hasattr(activity_tool, 'error_log'):
activity_tool.error_log.raising(sys.exc_info()) activity_tool.error_log.raising(sys.exc_info())
def validate(self, activity, activity_tool): def validate(self, activity, activity_tool, check_order_validation=1):
return activity.validate(activity_tool, self, **self.activity_kw) return activity.validate(activity_tool, self,
check_order_validation=check_order_validation,
**self.activity_kw)
def getDependentMessageList(self, activity, activity_tool):
return activity.getDependentMessageList(activity_tool, self, **self.activity_kw)
def notifyUser(self, activity_tool, message="Failed Processing Activity"): def notifyUser(self, activity_tool, message="Failed Processing Activity"):
"""Notify the user that the activity failed.""" """Notify the user that the activity failed."""
...@@ -477,9 +482,9 @@ class ActivityTool (Folder, UniqueObject): ...@@ -477,9 +482,9 @@ class ActivityTool (Folder, UniqueObject):
REQUEST.URL1 + REQUEST.URL1 +
'/manageLoadBalancing?manage_tabs_message=' + '/manageLoadBalancing?manage_tabs_message=' +
urllib.quote("Node(s) successfully deleted.")) urllib.quote("Node(s) successfully deleted."))
def process_timer(self, tick, interval, prev="", next=""): def process_timer(self, tick, interval, prev="", next=""):
""" """
Call distribute() if we are the Distributing Node and call tic() Call distribute() if we are the Distributing Node and call tic()
with our node number. with our node number.
This method is called by TimerService in the interval given This method is called by TimerService in the interval given
...@@ -489,23 +494,23 @@ class ActivityTool (Folder, UniqueObject): ...@@ -489,23 +494,23 @@ class ActivityTool (Folder, UniqueObject):
acquired = timerservice_lock.acquire(0) acquired = timerservice_lock.acquire(0)
if not acquired: if not acquired:
return return
old_sm = getSecurityManager() old_sm = getSecurityManager()
try: try:
# get owner of portal_catalog, so normally we should be able to # get owner of portal_catalog, so normally we should be able to
# have the permission to invoke all activities # have the permission to invoke all activities
user = self.portal_catalog.getWrappedOwner() user = self.portal_catalog.getWrappedOwner()
newSecurityManager(self.REQUEST, user) newSecurityManager(self.REQUEST, user)
currentNode = self.getCurrentNode() currentNode = self.getCurrentNode()
# only distribute when we are the distributingNode or if it's empty # only distribute when we are the distributingNode or if it's empty
if (self.distributingNode == self.getCurrentNode()): if (self.distributingNode == self.getCurrentNode()):
self.distribute(len(self._nodes)) self.distribute(len(self._nodes))
elif not self.distributingNode: elif not self.distributingNode:
self.distribute(1) self.distribute(1)
# SkinsTool uses a REQUEST cache to store skin objects, as # SkinsTool uses a REQUEST cache to store skin objects, as
# with TimerService we have the same REQUEST over multiple # with TimerService we have the same REQUEST over multiple
# portals, we clear this cache to make sure the cache doesn't # portals, we clear this cache to make sure the cache doesn't
...@@ -513,13 +518,13 @@ class ActivityTool (Folder, UniqueObject): ...@@ -513,13 +518,13 @@ class ActivityTool (Folder, UniqueObject):
stool = getToolByName(self, 'portal_skins', None) stool = getToolByName(self, 'portal_skins', None)
if stool is not None: if stool is not None:
stool.changeSkin(None) stool.changeSkin(None)
# call tic for the current processing_node # call tic for the current processing_node
# the processing_node numbers are the indices of the elements in the node tuple +1 # the processing_node numbers are the indices of the elements in the node tuple +1
# because processing_node starts form 1 # because processing_node starts form 1
if currentNode in self._nodes: if currentNode in self._nodes:
self.tic(list(self._nodes).index(currentNode)+1) self.tic(list(self._nodes).index(currentNode)+1)
elif len(self._nodes) == 0: elif len(self._nodes) == 0:
self.tic(1) self.tic(1)
...@@ -566,9 +571,9 @@ class ActivityTool (Folder, UniqueObject): ...@@ -566,9 +571,9 @@ class ActivityTool (Folder, UniqueObject):
# Initialize if needed # Initialize if needed
if not is_initialized: self.initialize() if not is_initialized: self.initialize()
inner_self = aq_inner(self) inner_self = aq_inner(self)
# If this is the first tic after zope is started, reset the processing # If this is the first tic after zope is started, reset the processing
# flag for activities of this node # flag for activities of this node
if first_run: if first_run:
...@@ -587,7 +592,7 @@ class ActivityTool (Folder, UniqueObject): ...@@ -587,7 +592,7 @@ class ActivityTool (Folder, UniqueObject):
raise raise
except: except:
LOG('CMFActivity:', 100, 'Core call to wakeup failed for activity %s' % activity) LOG('CMFActivity:', 100, 'Core call to wakeup failed for activity %s' % activity)
# Process messages on each queue in round robin # Process messages on each queue in round robin
has_awake_activity = 1 has_awake_activity = 1
while has_awake_activity: while has_awake_activity:
...@@ -673,7 +678,7 @@ class ActivityTool (Folder, UniqueObject): ...@@ -673,7 +678,7 @@ class ActivityTool (Folder, UniqueObject):
def invoke(self, message): def invoke(self, message):
message(self) message(self)
def invokeGroup(self, method_id, message_list): def invokeGroup(self, method_id, message_list):
# Invoke a group method. # Invoke a group method.
object_list = [] object_list = []
...@@ -770,7 +775,7 @@ class ActivityTool (Folder, UniqueObject): ...@@ -770,7 +775,7 @@ class ActivityTool (Folder, UniqueObject):
LOG('ActivityTool', WARNING, LOG('ActivityTool', WARNING,
'Could not call method %s on object %s' % ( 'Could not call method %s on object %s' % (
m.method_id, m.object_path), error=sys.exc_info()) m.method_id, m.object_path), error=sys.exc_info())
def newMessage(self, activity, path, active_process, def newMessage(self, activity, path, active_process,
activity_kw, method_id, *args, **kw): activity_kw, method_id, *args, **kw):
# Some Security Cheking should be made here XXX # Some Security Cheking should be made here XXX
...@@ -826,8 +831,8 @@ class ActivityTool (Folder, UniqueObject): ...@@ -826,8 +831,8 @@ class ActivityTool (Folder, UniqueObject):
LOG('ActivityTool', WARNING, LOG('ActivityTool', WARNING,
'could not dump messages from %s' % 'could not dump messages from %s' %
(activity,), error=sys.exc_info()) (activity,), error=sys.exc_info())
if hasattr(folder, 'SQLDict_createMessageTable'): if getattr(folder, 'SQLDict_createMessageTable', None) is not None:
try: try:
folder.SQLDict_dropMessageTable() folder.SQLDict_dropMessageTable()
except ConflictError: except ConflictError:
...@@ -838,7 +843,7 @@ class ActivityTool (Folder, UniqueObject): ...@@ -838,7 +843,7 @@ class ActivityTool (Folder, UniqueObject):
error=sys.exc_info()) error=sys.exc_info())
folder.SQLDict_createMessageTable() folder.SQLDict_createMessageTable()
if hasattr(folder, 'SQLQueue_createMessageTable'): if getattr(folder, 'SQLQueue_createMessageTable', None) is not None:
try: try:
folder.SQLQueue_dropMessageTable() folder.SQLQueue_dropMessageTable()
except ConflictError: except ConflictError:
...@@ -920,16 +925,24 @@ class ActivityTool (Folder, UniqueObject): ...@@ -920,16 +925,24 @@ class ActivityTool (Folder, UniqueObject):
self.immediateReindexObject() self.immediateReindexObject()
# Active synchronisation methods # Active synchronisation methods
security.declarePrivate('validateOrder')
def validateOrder(self, message, validator_id, validation_value): def validateOrder(self, message, validator_id, validation_value):
message_list = self.getDependentMessageList(message, validator_id, validation_value)
return len(message_list) > 0
security.declarePrivate('getDependentMessageList')
def getDependentMessageList(self, message, validator_id, validation_value):
global is_initialized global is_initialized
if not is_initialized: self.initialize() if not is_initialized: self.initialize()
message_list = []
for activity in activity_list: for activity in activity_list:
method_id = "_validate_%s" % validator_id method_id = "_validate_%s" % validator_id
if hasattr(activity, method_id): method = getattr(activity, method_id, None)
if getattr(activity,method_id)(aq_inner(self), if method is not None:
message, validation_value): result = method(aq_inner(self), message, validation_value)
return 1 if result:
return 0 message_list.extend([(activity, m) for m in result])
return message_list
# Required for tests (time shift) # Required for tests (time shift)
def timeShift(self, delay): def timeShift(self, delay):
......
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
title: title:
connection_id:cmf_activity_sql_connection connection_id:cmf_activity_sql_connection
max_rows:1000 max_rows:1000
max_cache:100 max_cache:0
cache_time:0 cache_time:0
class_name: class_name:
class_file: class_file:
</dtml-comment> </dtml-comment>
<params>processing_node:int=-1</params> <params>processing_node</params>
UPDATE UPDATE
message message
SET SET
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
title: title:
connection_id:cmf_activity_sql_connection connection_id:cmf_activity_sql_connection
max_rows:1000 max_rows:1000
max_cache:100 max_cache:0
cache_time:0 cache_time:0
class_name: class_name:
class_file: class_file:
......
...@@ -13,5 +13,6 @@ SET ...@@ -13,5 +13,6 @@ SET
processing_date = <dtml-sqlvar "_.DateTime()" type="datetime">, processing_date = <dtml-sqlvar "_.DateTime()" type="datetime">,
processing = 1 processing = 1
WHERE WHERE
<dtml-in uid>uid = <dtml-sqlvar sequence-item type="int"><dtml-if sequence-end><dtml-else> uid IN (
OR </dtml-if></dtml-in> <dtml-in uid><dtml-sqlvar sequence-item type="int"><dtml-if sequence-end><dtml-else>,</dtml-if></dtml-in>
)
...@@ -11,19 +11,9 @@ class_file: ...@@ -11,19 +11,9 @@ class_file:
method_id method_id
processing_node processing_node
priority priority
to_processing_date include_processing
include_processing</params> to_date</params>
<dtml-if to_processing_date>UPDATE message SELECT * FROM
SET
processing = 0
WHERE
processing = 1
AND
processing_date < <dtml-sqlvar to_processing_date type="datetime">
<dtml-var "'\0'">
</dtml-if>SELECT * FROM
message message
WHERE WHERE
1 = 1 1 = 1
...@@ -34,5 +24,6 @@ WHERE ...@@ -34,5 +24,6 @@ WHERE
<dtml-if priority> AND priority = <dtml-sqlvar priority type="int"> </dtml-if> <dtml-if priority> AND priority = <dtml-sqlvar priority type="int"> </dtml-if>
<dtml-if path>AND path = <dtml-sqlvar path type="string"> </dtml-if> <dtml-if path>AND path = <dtml-sqlvar path type="string"> </dtml-if>
<dtml-if method_id> AND method_id = <dtml-sqlvar method_id type="string"> </dtml-if> <dtml-if method_id> AND method_id = <dtml-sqlvar method_id type="string"> </dtml-if>
<dtml-if to_date> AND date <= <dtml-sqlvar to_date type="datetime"> </dtml-if>
ORDER BY ORDER BY
priority, date, uid priority, date, uid
<dtml-comment> <dtml-comment>
title: title:
connection_id:cmf_activity_sql_connection connection_id:cmf_activity_sql_connection
max_rows:10000 max_rows:1000
max_cache:100 max_cache:0
cache_time:0 cache_time:0
class_name: class_name:
class_file: class_file:
......
...@@ -11,32 +11,31 @@ class_file: ...@@ -11,32 +11,31 @@ class_file:
message_uid message_uid
path path
tag tag
count
</params> </params>
SELECT SELECT
COUNT(DISTINCT uid) as uid_count <dtml-if count>
COUNT(*) AS uid_count
<dtml-else>
*
</dtml-if>
FROM FROM
message message
WHERE WHERE
processing_node >= -2 processing_node >= -2
<dtml-if method_id> <dtml-if method_id>
AND ( AND method_id IN (
<dtml-in method_id> <dtml-in method_id><dtml-sqlvar sequence-item type="string"><dtml-if sequence-end><dtml-else>,</dtml-if></dtml-in>
method_id = <dtml-sqlvar sequence-item type="string"><dtml-if sequence-end><dtml-else> OR </dtml-if>
</dtml-in>
) )
</dtml-if> </dtml-if>
<dtml-if message_uid>AND uid = <dtml-sqlvar message_uid type="int"> </dtml-if> <dtml-if message_uid>AND uid = <dtml-sqlvar message_uid type="int"> </dtml-if>
<dtml-if path> <dtml-if path>
AND ( AND path IN (
<dtml-in path> <dtml-in path><dtml-sqlvar sequence-item type="string"><dtml-if sequence-end><dtml-else>,</dtml-if></dtml-in>
path = <dtml-sqlvar sequence-item type="string"><dtml-if sequence-end><dtml-else> OR </dtml-if>
</dtml-in>
) )
</dtml-if> </dtml-if>
<dtml-if tag> <dtml-if tag>
AND ( AND tag IN (
<dtml-in tag> <dtml-in tag><dtml-sqlvar sequence-item type="string"><dtml-if sequence-end><dtml-else>,</dtml-if></dtml-in>
tag = <dtml-sqlvar sequence-item type="string"><dtml-if sequence-end><dtml-else> OR </dtml-if>
</dtml-in>
) )
</dtml-if> </dtml-if>
...@@ -11,7 +11,7 @@ class_file: ...@@ -11,7 +11,7 @@ class_file:
processing_node processing_node
method_id method_id
broadcast broadcast
uid:int=0</params> uid</params>
UPDATE message_queue UPDATE message_queue
SET SET
processing_node=<dtml-sqlvar processing_node type="int">, processing_node=<dtml-sqlvar processing_node type="int">,
......
...@@ -7,7 +7,7 @@ cache_time:0 ...@@ -7,7 +7,7 @@ cache_time:0
class_name: class_name:
class_file: class_file:
</dtml-comment> </dtml-comment>
<params>processing_node:int=-1</params> <params>processing_node</params>
UPDATE UPDATE
message_queue message_queue
SET SET
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
title: title:
connection_id:cmf_activity_sql_connection connection_id:cmf_activity_sql_connection
max_rows:1000 max_rows:1000
max_cache:100 max_cache:0
cache_time:0 cache_time:0
class_name: class_name:
class_file: class_file:
......
...@@ -19,4 +19,4 @@ WHERE ...@@ -19,4 +19,4 @@ WHERE
<dtml-if to_date> AND date <= <dtml-sqlvar to_date type="datetime"> </dtml-if> <dtml-if to_date> AND date <= <dtml-sqlvar to_date type="datetime"> </dtml-if>
ORDER BY ORDER BY
priority, date priority, date, uid
...@@ -10,7 +10,8 @@ class_file: ...@@ -10,7 +10,8 @@ class_file:
<params>path <params>path
method_id method_id
processing_node processing_node
priority</params> priority
to_date</params>
SELECT * FROM SELECT * FROM
message_queue message_queue
WHERE WHERE
...@@ -19,3 +20,6 @@ WHERE ...@@ -19,3 +20,6 @@ WHERE
<dtml-if priority>AND priority = <dtml-sqlvar priority type="int"> </dtml-if> <dtml-if priority>AND priority = <dtml-sqlvar priority type="int"> </dtml-if>
<dtml-if path>AND path = <dtml-sqlvar path type="string"></dtml-if> <dtml-if path>AND path = <dtml-sqlvar path type="string"></dtml-if>
<dtml-if method_id>AND method_id = <dtml-sqlvar method_id type="string"></dtml-if> <dtml-if method_id>AND method_id = <dtml-sqlvar method_id type="string"></dtml-if>
<dtml-if to_date> AND date <= <dtml-sqlvar to_date type="datetime"> </dtml-if>
ORDER BY
priority, date, uid
<dtml-comment> <dtml-comment>
title: title:
connection_id:cmf_activity_sql_connection connection_id:cmf_activity_sql_connection
max_rows:10000 max_rows:1000
max_cache:100 max_cache:0
cache_time:0 cache_time:0
class_name: class_name:
class_file: class_file:
...@@ -16,5 +16,5 @@ SELECT uid FROM ...@@ -16,5 +16,5 @@ SELECT uid FROM
WHERE WHERE
processing <> 1 processing <> 1
<dtml-if processing_node> AND processing_node = <dtml-sqlvar processing_node type="int"></dtml-if> <dtml-if processing_node> AND processing_node = <dtml-sqlvar processing_node type="int"></dtml-if>
<dtml-if path>AND path = <dtml-sqlvar path type="string"></dtml-if> <dtml-if path> AND path = <dtml-sqlvar path type="string"></dtml-if>
<dtml-if to_date>AND date <= <dtml-sqlvar to_date type="datetime"> </dtml-if> <dtml-if to_date> AND date <= <dtml-sqlvar to_date type="datetime"> </dtml-if>
...@@ -11,10 +11,10 @@ class_file: ...@@ -11,10 +11,10 @@ class_file:
priority priority
date</params> date</params>
UPDATE UPDATE
message_queue message_queue
SET SET
priority = <dtml-sqlvar priority type="int">, priority = <dtml-sqlvar priority type="int">,
processing = 0, processing = 0,
date = <dtml-sqlvar date type="datetime"> date = <dtml-sqlvar date type="datetime">
WHERE WHERE
uid = <dtml-sqlvar uid type="int"> uid = <dtml-sqlvar uid type="int">
...@@ -11,32 +11,31 @@ class_file: ...@@ -11,32 +11,31 @@ class_file:
message_uid message_uid
path path
tag tag
count
</params> </params>
SELECT SELECT
COUNT(DISTINCT uid) as uid_count <dtml-if count>
COUNT(*) AS uid_count
<dtml-else>
*
</dtml-if>
FROM FROM
message_queue message_queue
WHERE WHERE
processing_node >= -2 processing_node >= -2
<dtml-if method_id> <dtml-if method_id>
AND ( AND method_id IN (
<dtml-in method_id> <dtml-in method_id><dtml-sqlvar sequence-item type="string"><dtml-if sequence-end><dtml-else>,</dtml-if></dtml-in>
method_id = <dtml-sqlvar sequence-item type="string"><dtml-if sequence-end><dtml-else> OR </dtml-if>
</dtml-in>
) )
</dtml-if> </dtml-if>
<dtml-if message_uid>AND uid = <dtml-sqlvar message_uid type="int"> </dtml-if> <dtml-if message_uid>AND uid = <dtml-sqlvar message_uid type="int"> </dtml-if>
<dtml-if path> <dtml-if path>
AND ( AND path IN (
<dtml-in path> <dtml-in path><dtml-sqlvar sequence-item type="string"><dtml-if sequence-end><dtml-else>,</dtml-if></dtml-in>
path = <dtml-sqlvar sequence-item type="string"><dtml-if sequence-end><dtml-else> OR </dtml-if>
</dtml-in>
) )
</dtml-if> </dtml-if>
<dtml-if tag> <dtml-if tag>
AND ( AND tag IN (
<dtml-in tag> <dtml-in tag><dtml-sqlvar sequence-item type="string"><dtml-if sequence-end><dtml-else>,</dtml-if></dtml-in>
tag = <dtml-sqlvar sequence-item type="string"><dtml-if sequence-end><dtml-else> OR </dtml-if>
</dtml-in>
) )
</dtml-if> </dtml-if>
...@@ -13,18 +13,20 @@ method_id ...@@ -13,18 +13,20 @@ method_id
message message
priority priority
broadcast broadcast
processing_node=-1 processing_node
date date
tag</params> tag</params>
INSERT INTO message_queue INSERT INTO message_queue
SET SET
uid = <dtml-sqlvar uid type="int">, uid = <dtml-sqlvar uid type="int">,
path = <dtml-sqlvar path type="string">, path = <dtml-sqlvar path type="string">,
<dtml-if date>date = <dtml-sqlvar date type="datetime">, <dtml-else>date = <dtml-sqlvar "_.DateTime()" type="datetime">, </dtml-if> <dtml-if date>date = <dtml-sqlvar date type="datetime">, <dtml-else>date = <dtml-sqlvar "_.DateTime()" type="datetime">, </dtml-if>
method_id = <dtml-sqlvar method_id type="string">, method_id = <dtml-sqlvar method_id type="string">,
processing_node = <dtml-sqlvar processing_node type="int">, <dtml-if processing_node>
broadcast = <dtml-sqlvar broadcast type="int">, processing_node = <dtml-sqlvar processing_node type="int">,
processing = -1, </dtml-if>
priority = <dtml-sqlvar priority type="int">, broadcast = <dtml-sqlvar broadcast type="int">,
tag = <dtml-sqlvar tag type="string">, processing = -1,
message = <dtml-sqlvar message type="string"> priority = <dtml-sqlvar priority type="int">,
tag = <dtml-sqlvar tag type="string">,
message = <dtml-sqlvar message type="string">
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