Use queue's name and project to get messages
Mongodb's driver currently queries the queue's collection to get queue id and then filter messages with it. Instead of storing the id, this patches stores the queue name and the project in the message collection and use that for querying messages. It's safe to assume that the queue exists when messages are returned by the query, however, posts operation will still verify queue's existence. Fixes bug: #1207018 Change-Id: Iaa7bc3f1300b3349a5cfad5a9f6ecabb2f75e95e
This commit is contained in:
@@ -64,9 +64,6 @@ class ClaimController(storage.ClaimBase):
|
|||||||
def get(self, queue, claim_id, project=None):
|
def get(self, queue, claim_id, project=None):
|
||||||
msg_ctrl = self.driver.message_controller
|
msg_ctrl = self.driver.message_controller
|
||||||
|
|
||||||
# Check whether the queue exists or not
|
|
||||||
qid = self._get_queue_id(queue, project)
|
|
||||||
|
|
||||||
# Base query, always check expire time
|
# Base query, always check expire time
|
||||||
now = timeutils.utcnow()
|
now = timeutils.utcnow()
|
||||||
|
|
||||||
@@ -91,8 +88,9 @@ class ClaimController(storage.ClaimBase):
|
|||||||
# Lets get claim's data
|
# Lets get claim's data
|
||||||
# from the first message
|
# from the first message
|
||||||
# in the iterator
|
# in the iterator
|
||||||
messages = messages(msg_ctrl.claimed(qid, cid, now))
|
msgs = messages(msg_ctrl.claimed(queue, cid, now,
|
||||||
claim = next(messages)
|
project=project))
|
||||||
|
claim = next(msgs)
|
||||||
claim = {
|
claim = {
|
||||||
'age': age.seconds,
|
'age': age.seconds,
|
||||||
'ttl': claim.pop('t'),
|
'ttl': claim.pop('t'),
|
||||||
@@ -101,7 +99,7 @@ class ClaimController(storage.ClaimBase):
|
|||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise exceptions.ClaimDoesNotExist(cid, queue, project)
|
raise exceptions.ClaimDoesNotExist(cid, queue, project)
|
||||||
|
|
||||||
return (claim, messages)
|
return (claim, msgs)
|
||||||
|
|
||||||
@utils.raises_conn_error
|
@utils.raises_conn_error
|
||||||
def create(self, queue, metadata, project=None, limit=10):
|
def create(self, queue, metadata, project=None, limit=10):
|
||||||
@@ -124,9 +122,8 @@ class ClaimController(storage.ClaimBase):
|
|||||||
"""
|
"""
|
||||||
msg_ctrl = self.driver.message_controller
|
msg_ctrl = self.driver.message_controller
|
||||||
|
|
||||||
# We don't need the qid here but
|
self._get_queue_id(queue, project)
|
||||||
# we need to verify it exists.
|
|
||||||
qid = self._get_queue_id(queue, project)
|
|
||||||
ttl = metadata['ttl']
|
ttl = metadata['ttl']
|
||||||
grace = metadata['grace']
|
grace = metadata['grace']
|
||||||
oid = objectid.ObjectId()
|
oid = objectid.ObjectId()
|
||||||
@@ -147,7 +144,7 @@ class ClaimController(storage.ClaimBase):
|
|||||||
|
|
||||||
# Get a list of active, not claimed nor expired
|
# Get a list of active, not claimed nor expired
|
||||||
# messages that could be claimed.
|
# messages that could be claimed.
|
||||||
msgs = msg_ctrl.active(qid, fields={'_id': 1})
|
msgs = msg_ctrl.active(queue, fields={'_id': 1}, project=project)
|
||||||
msgs = msgs.limit(limit)
|
msgs = msgs.limit(limit)
|
||||||
|
|
||||||
messages = iter([])
|
messages = iter([])
|
||||||
@@ -177,7 +174,8 @@ class ClaimController(storage.ClaimBase):
|
|||||||
# `expires` on messages that would
|
# `expires` on messages that would
|
||||||
# expire before claim.
|
# expire before claim.
|
||||||
new_values = {'e': message_expires, 't': message_ttl}
|
new_values = {'e': message_expires, 't': message_ttl}
|
||||||
msg_ctrl._col.update({'q': qid,
|
msg_ctrl._col.update({'q': queue,
|
||||||
|
'p': project,
|
||||||
'e': {'$lt': message_expires},
|
'e': {'$lt': message_expires},
|
||||||
'c.id': oid},
|
'c.id': oid},
|
||||||
{'$set': new_values},
|
{'$set': new_values},
|
||||||
@@ -204,9 +202,9 @@ class ClaimController(storage.ClaimBase):
|
|||||||
if now > expires:
|
if now > expires:
|
||||||
raise ValueError('New ttl will make the claim expires')
|
raise ValueError('New ttl will make the claim expires')
|
||||||
|
|
||||||
qid = self._get_queue_id(queue, project)
|
|
||||||
msg_ctrl = self.driver.message_controller
|
msg_ctrl = self.driver.message_controller
|
||||||
claimed = msg_ctrl.claimed(qid, cid, expires=now, limit=1)
|
claimed = msg_ctrl.claimed(queue, cid, expires=now,
|
||||||
|
limit=1, project=project)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
next(claimed)
|
next(claimed)
|
||||||
@@ -219,7 +217,7 @@ class ClaimController(storage.ClaimBase):
|
|||||||
'e': expires,
|
'e': expires,
|
||||||
}
|
}
|
||||||
|
|
||||||
msg_ctrl._col.update({'q': qid, 'c.id': cid},
|
msg_ctrl._col.update({'q': queue, 'p': project, 'c.id': cid},
|
||||||
{'$set': {'c': meta}},
|
{'$set': {'c': meta}},
|
||||||
upsert=False, multi=True)
|
upsert=False, multi=True)
|
||||||
|
|
||||||
@@ -227,7 +225,8 @@ class ClaimController(storage.ClaimBase):
|
|||||||
# This sets the expiration time to
|
# This sets the expiration time to
|
||||||
# `expires` on messages that would
|
# `expires` on messages that would
|
||||||
# expire before claim.
|
# expire before claim.
|
||||||
msg_ctrl._col.update({'q': qid,
|
msg_ctrl._col.update({'q': queue,
|
||||||
|
'p': project,
|
||||||
'e': {'$lt': expires},
|
'e': {'$lt': expires},
|
||||||
'c.id': cid},
|
'c.id': cid},
|
||||||
{'$set': {'e': expires, 't': ttl}},
|
{'$set': {'e': expires, 't': ttl}},
|
||||||
@@ -235,11 +234,5 @@ class ClaimController(storage.ClaimBase):
|
|||||||
|
|
||||||
@utils.raises_conn_error
|
@utils.raises_conn_error
|
||||||
def delete(self, queue, claim_id, project=None):
|
def delete(self, queue, claim_id, project=None):
|
||||||
try:
|
|
||||||
qid = self._get_queue_id(queue, project)
|
|
||||||
except exceptions.QueueDoesNotExist:
|
|
||||||
# Fail silently on bad queue/project
|
|
||||||
return
|
|
||||||
|
|
||||||
msg_ctrl = self.driver.message_controller
|
msg_ctrl = self.driver.message_controller
|
||||||
msg_ctrl.unclaim(qid, claim_id)
|
msg_ctrl.unclaim(queue, claim_id, project=project)
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import collections
|
|||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from bson import objectid
|
|
||||||
import pymongo.errors
|
import pymongo.errors
|
||||||
|
|
||||||
import marconi.openstack.common.log as logging
|
import marconi.openstack.common.log as logging
|
||||||
@@ -75,6 +74,7 @@ class MessageController(storage.MessageBase):
|
|||||||
# specific message. (see `get`)
|
# specific message. (see `get`)
|
||||||
self.active_fields = [
|
self.active_fields = [
|
||||||
('q', 1),
|
('q', 1),
|
||||||
|
('p', 1),
|
||||||
('k', 1),
|
('k', 1),
|
||||||
('e', 1),
|
('e', 1),
|
||||||
('c.e', 1),
|
('c.e', 1),
|
||||||
@@ -87,6 +87,7 @@ class MessageController(storage.MessageBase):
|
|||||||
# Index used for claims
|
# Index used for claims
|
||||||
self.claimed_fields = [
|
self.claimed_fields = [
|
||||||
('q', 1),
|
('q', 1),
|
||||||
|
('p', 1),
|
||||||
('c.id', 1),
|
('c.id', 1),
|
||||||
('k', 1),
|
('k', 1),
|
||||||
('c.e', 1),
|
('c.e', 1),
|
||||||
@@ -106,7 +107,7 @@ class MessageController(storage.MessageBase):
|
|||||||
# to miss a message when there is more than one
|
# to miss a message when there is more than one
|
||||||
# producer posting messages to the same queue, in
|
# producer posting messages to the same queue, in
|
||||||
# parallel.
|
# parallel.
|
||||||
self._col.ensure_index([('q', 1), ('k', -1)],
|
self._col.ensure_index([('q', 1), ('p', 1), ('k', -1)],
|
||||||
name='queue_marker',
|
name='queue_marker',
|
||||||
unique=True,
|
unique=True,
|
||||||
background=True)
|
background=True)
|
||||||
@@ -118,10 +119,10 @@ class MessageController(storage.MessageBase):
|
|||||||
def _get_queue_id(self, queue, project=None):
|
def _get_queue_id(self, queue, project=None):
|
||||||
return self._queue_controller._get_id(queue, project)
|
return self._queue_controller._get_id(queue, project)
|
||||||
|
|
||||||
def _get_queue_ids(self):
|
def _get_queue_np(self):
|
||||||
return self._queue_controller._get_ids()
|
return self._queue_controller._get_np()
|
||||||
|
|
||||||
def _next_marker(self, queue_id):
|
def _next_marker(self, queue, project=None):
|
||||||
"""Retrieves the next message marker for a given queue.
|
"""Retrieves the next message marker for a given queue.
|
||||||
|
|
||||||
This helper is used to generate monotonic pagination
|
This helper is used to generate monotonic pagination
|
||||||
@@ -140,11 +141,12 @@ class MessageController(storage.MessageBase):
|
|||||||
mitigate race conditions between producer and
|
mitigate race conditions between producer and
|
||||||
observer clients.
|
observer clients.
|
||||||
|
|
||||||
:param queue_id: queue ID
|
:param queue: queue name
|
||||||
|
:param project: Queue's project
|
||||||
:returns: next message marker as an integer
|
:returns: next message marker as an integer
|
||||||
"""
|
"""
|
||||||
|
|
||||||
document = self._col.find_one({'q': queue_id},
|
document = self._col.find_one({'q': queue, 'p': project},
|
||||||
sort=[('k', -1)],
|
sort=[('k', -1)],
|
||||||
fields={'k': 1, '_id': 0})
|
fields={'k': 1, '_id': 0})
|
||||||
|
|
||||||
@@ -166,20 +168,21 @@ class MessageController(storage.MessageBase):
|
|||||||
|
|
||||||
time.sleep(seconds)
|
time.sleep(seconds)
|
||||||
|
|
||||||
def _count_expired(self, queue_id):
|
def _count_expired(self, queue_name, project=None):
|
||||||
"""Counts the number of expired messages in a queue.
|
"""Counts the number of expired messages in a queue.
|
||||||
|
|
||||||
:param queue_id: id for the queue to stat
|
:param queue_id: id for the queue to stat
|
||||||
"""
|
"""
|
||||||
|
|
||||||
query = {
|
query = {
|
||||||
'q': queue_id,
|
'p': project,
|
||||||
|
'q': queue_name,
|
||||||
'e': {'$lte': timeutils.utcnow()},
|
'e': {'$lte': timeutils.utcnow()},
|
||||||
}
|
}
|
||||||
|
|
||||||
return self._col.find(query).count()
|
return self._col.find(query).count()
|
||||||
|
|
||||||
def _remove_expired(self, queue_id):
|
def _remove_expired(self, queue_name, project):
|
||||||
"""Removes all expired messages except for the most recent
|
"""Removes all expired messages except for the most recent
|
||||||
in each queue.
|
in each queue.
|
||||||
|
|
||||||
@@ -190,28 +193,32 @@ class MessageController(storage.MessageBase):
|
|||||||
Note that expired messages are only removed if their count
|
Note that expired messages are only removed if their count
|
||||||
exceeds options.CFG.gc_threshold.
|
exceeds options.CFG.gc_threshold.
|
||||||
|
|
||||||
:param queue_id: id for the queue from which to remove
|
:param queue_name: name for the queue from which to remove
|
||||||
expired messages
|
expired messages
|
||||||
|
:param project: Project queue_name belong's too
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if options.CFG.gc_threshold <= self._count_expired(queue_id):
|
expired_msgs = self._count_expired(queue_name, project)
|
||||||
|
if options.CFG.gc_threshold <= expired_msgs:
|
||||||
# Get the message with the highest marker, and leave
|
# Get the message with the highest marker, and leave
|
||||||
# it in the queue
|
# it in the queue
|
||||||
head = self._col.find_one({'q': queue_id},
|
|
||||||
sort=[('k', -1)],
|
# NOTE(flaper87): Keep the counter in a separate record and
|
||||||
fields={'_id': 1})
|
# lets remove all messages.
|
||||||
|
head = self._col.find_one({'q': queue_name, 'p': project},
|
||||||
|
sort=[('k', -1)], fields={'_id': 1})
|
||||||
|
|
||||||
if head is None:
|
if head is None:
|
||||||
# Assume queue was just deleted via a parallel request
|
# Assume queue was just deleted via a parallel request
|
||||||
LOG.warning(_('Queue %s is empty or missing.') % queue_id)
|
LOG.warning(_('Queue %s is empty or missing.') % queue_name)
|
||||||
return
|
return
|
||||||
|
|
||||||
# NOTE(flaper87): Can we use k instead of
|
# NOTE(flaper87): Can we use k instead of
|
||||||
# _id here? The active index will cover
|
# _id here? The active index will cover
|
||||||
# the previous query and the the remove
|
# the previous query and the remove one.
|
||||||
# one.
|
|
||||||
query = {
|
query = {
|
||||||
'q': queue_id,
|
'p': project,
|
||||||
|
'q': queue_name,
|
||||||
'e': {'$lte': timeutils.utcnow()},
|
'e': {'$lte': timeutils.utcnow()},
|
||||||
'_id': {'$ne': head['_id']}
|
'_id': {'$ne': head['_id']}
|
||||||
}
|
}
|
||||||
@@ -230,17 +237,14 @@ class MessageController(storage.MessageBase):
|
|||||||
:param queue: name of the queue to purge
|
:param queue: name of the queue to purge
|
||||||
:param project: name of the project to which the queue belongs
|
:param project: name of the project to which the queue belongs
|
||||||
"""
|
"""
|
||||||
try:
|
self._col.remove({'q': queue, 'p': project}, w=0)
|
||||||
qid = self._get_queue_id(queue, project)
|
|
||||||
self._col.remove({'q': qid}, w=0)
|
|
||||||
except exceptions.QueueDoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _list(self, queue_id, marker=None, echo=False,
|
def _list(self, queue_name, marker=None, echo=False, client_uuid=None,
|
||||||
client_uuid=None, fields=None, include_claimed=False):
|
fields=None, include_claimed=False, project=None):
|
||||||
"""Message document listing helper.
|
"""Message document listing helper.
|
||||||
|
|
||||||
:param queue_id: ObjectID of the queue to list
|
:param queue_name: Name of the queue to list
|
||||||
|
:param project: Project `queue_name` belongs to.
|
||||||
:param marker: Message marker from which to start iterating
|
:param marker: Message marker from which to start iterating
|
||||||
:param echo: Whether to return messages that match client_uuid
|
:param echo: Whether to return messages that match client_uuid
|
||||||
:param client_uuid: UUID for the client that originated this request
|
:param client_uuid: UUID for the client that originated this request
|
||||||
@@ -254,9 +258,12 @@ class MessageController(storage.MessageBase):
|
|||||||
now = timeutils.utcnow()
|
now = timeutils.utcnow()
|
||||||
|
|
||||||
query = {
|
query = {
|
||||||
# Messages must belong to this queue
|
# Messages must belong to this
|
||||||
'q': queue_id,
|
# queue and project
|
||||||
# The messages can not be expired
|
'p': project,
|
||||||
|
'q': queue_name,
|
||||||
|
|
||||||
|
# The messages cannot be expired
|
||||||
'e': {'$gt': now},
|
'e': {'$gt': now},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,26 +289,20 @@ class MessageController(storage.MessageBase):
|
|||||||
# Interface
|
# Interface
|
||||||
#-----------------------------------------------------------------------
|
#-----------------------------------------------------------------------
|
||||||
|
|
||||||
def active(self, queue_id, marker=None, echo=False,
|
def active(self, queue_name, marker=None, echo=False,
|
||||||
client_uuid=None, fields=None):
|
client_uuid=None, fields=None, project=None):
|
||||||
|
|
||||||
# NOTE(kgriffs): Since this is a public method, queue_id
|
return self._list(queue_name, marker, echo, client_uuid,
|
||||||
# might not be an ObjectID. Usually it will be, since active()
|
fields, include_claimed=False, project=project)
|
||||||
# is a utility method, so short-circuit for performance.
|
|
||||||
if not isinstance(queue_id, objectid.ObjectId):
|
|
||||||
queue_id = utils.to_oid(queue_id)
|
|
||||||
|
|
||||||
return self._list(queue_id, marker, echo, client_uuid, fields,
|
def claimed(self, queue_name, claim_id=None,
|
||||||
include_claimed=False)
|
expires=None, limit=None, project=None):
|
||||||
|
|
||||||
def claimed(self, queue_id, claim_id=None, expires=None, limit=None):
|
|
||||||
if not isinstance(queue_id, objectid.ObjectId):
|
|
||||||
queue_id = utils.to_oid(queue_id)
|
|
||||||
|
|
||||||
query = {
|
query = {
|
||||||
'c.id': claim_id,
|
'c.id': claim_id,
|
||||||
'c.e': {'$gt': expires or timeutils.utcnow()},
|
'c.e': {'$gt': expires or timeutils.utcnow()},
|
||||||
'q': queue_id,
|
'q': queue_name,
|
||||||
|
'p': project,
|
||||||
}
|
}
|
||||||
|
|
||||||
if not claim_id:
|
if not claim_id:
|
||||||
@@ -323,18 +324,17 @@ class MessageController(storage.MessageBase):
|
|||||||
|
|
||||||
return utils.HookedCursor(msgs, denormalizer)
|
return utils.HookedCursor(msgs, denormalizer)
|
||||||
|
|
||||||
def unclaim(self, queue_id, claim_id):
|
def unclaim(self, queue_name, claim_id, project=None):
|
||||||
try:
|
try:
|
||||||
qid = utils.to_oid(queue_id)
|
|
||||||
cid = utils.to_oid(claim_id)
|
cid = utils.to_oid(claim_id)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._col.update({'q': qid, 'c.id': cid},
|
self._col.update({'q': queue_name, 'p': project, 'c.id': cid},
|
||||||
{'$set': {'c': {'id': None, 'e': 0}}},
|
{'$set': {'c': {'id': None, 'e': 0}}},
|
||||||
upsert=False, multi=True)
|
upsert=False, multi=True)
|
||||||
|
|
||||||
def remove_expired(self, project=None):
|
def remove_expired(self):
|
||||||
"""Removes all expired messages except for the most recent
|
"""Removes all expired messages except for the most recent
|
||||||
in each queue.
|
in each queue.
|
||||||
|
|
||||||
@@ -359,8 +359,8 @@ class MessageController(storage.MessageBase):
|
|||||||
# each message inserted (TBD, may cause problematic side-effect),
|
# each message inserted (TBD, may cause problematic side-effect),
|
||||||
# and third, by changing the marker algorithm such that it no
|
# and third, by changing the marker algorithm such that it no
|
||||||
# longer depends on retaining the last message in the queue!
|
# longer depends on retaining the last message in the queue!
|
||||||
for id in self._get_queue_ids():
|
for name, project in self._get_queue_np():
|
||||||
self._remove_expired(id)
|
self._remove_expired(name, project)
|
||||||
|
|
||||||
def list(self, queue, project=None, marker=None, limit=10,
|
def list(self, queue, project=None, marker=None, limit=10,
|
||||||
echo=False, client_uuid=None, include_claimed=False):
|
echo=False, client_uuid=None, include_claimed=False):
|
||||||
@@ -371,9 +371,8 @@ class MessageController(storage.MessageBase):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
raise exceptions.MalformedMarker()
|
raise exceptions.MalformedMarker()
|
||||||
|
|
||||||
qid = self._get_queue_id(queue, project)
|
messages = self._list(queue, marker, echo, client_uuid,
|
||||||
messages = self._list(qid, marker, echo, client_uuid,
|
include_claimed=include_claimed, project=project)
|
||||||
include_claimed=include_claimed)
|
|
||||||
|
|
||||||
messages = messages.limit(limit)
|
messages = messages.limit(limit)
|
||||||
marker_id = {}
|
marker_id = {}
|
||||||
@@ -393,19 +392,19 @@ class MessageController(storage.MessageBase):
|
|||||||
mid = utils.to_oid(message_id)
|
mid = utils.to_oid(message_id)
|
||||||
now = timeutils.utcnow()
|
now = timeutils.utcnow()
|
||||||
|
|
||||||
# Base query, always check expire time
|
|
||||||
query = {
|
query = {
|
||||||
'q': self._get_queue_id(queue, project),
|
'_id': mid,
|
||||||
'e': {'$gt': now},
|
'q': queue,
|
||||||
'_id': mid
|
'p': project,
|
||||||
|
'e': {'$gt': now}
|
||||||
}
|
}
|
||||||
|
|
||||||
message = self._col.find_one(query)
|
message = list(self._col.find(query).limit(1).hint([('_id', 1)]))
|
||||||
|
|
||||||
if message is None:
|
if not message:
|
||||||
raise exceptions.MessageDoesNotExist(message_id, queue, project)
|
raise exceptions.MessageDoesNotExist(message_id, queue, project)
|
||||||
|
|
||||||
return _basic_message(message, now)
|
return _basic_message(message[0], now)
|
||||||
|
|
||||||
@utils.raises_conn_error
|
@utils.raises_conn_error
|
||||||
def bulk_get(self, queue, message_ids, project=None):
|
def bulk_get(self, queue, message_ids, project=None):
|
||||||
@@ -414,12 +413,15 @@ class MessageController(storage.MessageBase):
|
|||||||
|
|
||||||
# Base query, always check expire time
|
# Base query, always check expire time
|
||||||
query = {
|
query = {
|
||||||
'q': self._get_queue_id(queue, project),
|
'q': queue,
|
||||||
'e': {'$gt': now},
|
'p': project,
|
||||||
'_id': {'$in': message_ids},
|
'_id': {'$in': message_ids},
|
||||||
|
'e': {'$gt': now},
|
||||||
}
|
}
|
||||||
|
|
||||||
messages = self._col.find(query)
|
# NOTE(flaper87): Should this query
|
||||||
|
# be sorted?
|
||||||
|
messages = self._col.find(query).hint([('_id', 1)])
|
||||||
|
|
||||||
def denormalizer(msg):
|
def denormalizer(msg):
|
||||||
return _basic_message(msg, now)
|
return _basic_message(msg, now)
|
||||||
@@ -429,10 +431,12 @@ class MessageController(storage.MessageBase):
|
|||||||
@utils.raises_conn_error
|
@utils.raises_conn_error
|
||||||
def post(self, queue, messages, client_uuid, project=None):
|
def post(self, queue, messages, client_uuid, project=None):
|
||||||
now = timeutils.utcnow()
|
now = timeutils.utcnow()
|
||||||
queue_id = self._get_queue_id(queue, project)
|
|
||||||
|
# NOTE(flaper87): We need to assert the queue exists
|
||||||
|
self._get_queue_id(queue, project)
|
||||||
|
|
||||||
# Set the next basis marker for the first attempt.
|
# Set the next basis marker for the first attempt.
|
||||||
next_marker = self._next_marker(queue_id)
|
next_marker = self._next_marker(queue, project)
|
||||||
|
|
||||||
# Results are aggregated across all attempts
|
# Results are aggregated across all attempts
|
||||||
# NOTE(kgriffs): lazy instantiation
|
# NOTE(kgriffs): lazy instantiation
|
||||||
@@ -447,7 +451,8 @@ class MessageController(storage.MessageBase):
|
|||||||
message_gen = (
|
message_gen = (
|
||||||
{
|
{
|
||||||
't': message['ttl'],
|
't': message['ttl'],
|
||||||
'q': queue_id,
|
'q': queue,
|
||||||
|
'p': project,
|
||||||
'e': now + datetime.timedelta(seconds=message['ttl']),
|
'e': now + datetime.timedelta(seconds=message['ttl']),
|
||||||
'u': client_uuid,
|
'u': client_uuid,
|
||||||
'c': {'id': None, 'e': now},
|
'c': {'id': None, 'e': now},
|
||||||
@@ -477,9 +482,9 @@ class MessageController(storage.MessageBase):
|
|||||||
if attempt != 0:
|
if attempt != 0:
|
||||||
message = _('%(attempts)d attempt(s) required to post '
|
message = _('%(attempts)d attempt(s) required to post '
|
||||||
'%(num_messages)d messages to queue '
|
'%(num_messages)d messages to queue '
|
||||||
'%(queue_id)s')
|
'%(queue_name)s and project %(project)s')
|
||||||
message %= dict(queue_id=queue_id, attempts=attempt + 1,
|
message %= dict(queue_name=queue, attempts=attempt + 1,
|
||||||
num_messages=len(ids))
|
num_messages=len(ids), project=project)
|
||||||
|
|
||||||
LOG.debug(message)
|
LOG.debug(message)
|
||||||
|
|
||||||
@@ -496,7 +501,7 @@ class MessageController(storage.MessageBase):
|
|||||||
# TODO(kgriffs): Add transaction ID to help match up loglines
|
# TODO(kgriffs): Add transaction ID to help match up loglines
|
||||||
if attempt == 0:
|
if attempt == 0:
|
||||||
message = _('First attempt failed while adding messages '
|
message = _('First attempt failed while adding messages '
|
||||||
'to queue %s for current request') % queue_id
|
'to queue %s for current request') % queue
|
||||||
|
|
||||||
LOG.debug(message)
|
LOG.debug(message)
|
||||||
|
|
||||||
@@ -530,7 +535,7 @@ class MessageController(storage.MessageBase):
|
|||||||
# Retry the remaining messages with a new sequence
|
# Retry the remaining messages with a new sequence
|
||||||
# of markers.
|
# of markers.
|
||||||
prepared_messages = cached_messages[failed_index:]
|
prepared_messages = cached_messages[failed_index:]
|
||||||
next_marker = self._next_marker(queue_id)
|
next_marker = self._next_marker(queue, project)
|
||||||
for index, message in enumerate(prepared_messages):
|
for index, message in enumerate(prepared_messages):
|
||||||
message['k'] = next_marker + index
|
message['k'] = next_marker + index
|
||||||
|
|
||||||
@@ -548,7 +553,7 @@ class MessageController(storage.MessageBase):
|
|||||||
|
|
||||||
message = _('Hit maximum number of attempts (%(max)s) for queue '
|
message = _('Hit maximum number of attempts (%(max)s) for queue '
|
||||||
'%(id)s in project %(project)s')
|
'%(id)s in project %(project)s')
|
||||||
message %= dict(max=options.CFG.max_attempts, id=queue_id,
|
message %= dict(max=options.CFG.max_attempts, id=queue,
|
||||||
project=project)
|
project=project)
|
||||||
|
|
||||||
LOG.warning(message)
|
LOG.warning(message)
|
||||||
@@ -562,7 +567,8 @@ class MessageController(storage.MessageBase):
|
|||||||
mid = utils.to_oid(message_id)
|
mid = utils.to_oid(message_id)
|
||||||
|
|
||||||
query = {
|
query = {
|
||||||
'q': self._get_queue_id(queue, project),
|
'q': queue,
|
||||||
|
'p': project,
|
||||||
'_id': mid
|
'_id': mid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -592,7 +598,8 @@ class MessageController(storage.MessageBase):
|
|||||||
try:
|
try:
|
||||||
message_ids = [utils.to_oid(id) for id in message_ids]
|
message_ids = [utils.to_oid(id) for id in message_ids]
|
||||||
query = {
|
query = {
|
||||||
'q': self._get_queue_id(queue, project),
|
'q': queue,
|
||||||
|
'p': project,
|
||||||
'_id': {'$in': message_ids},
|
'_id': {'$in': message_ids},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,10 +74,10 @@ class QueueController(storage.QueueBase):
|
|||||||
queue = self._get(name, project, fields=['_id'])
|
queue = self._get(name, project, fields=['_id'])
|
||||||
return queue.get('_id')
|
return queue.get('_id')
|
||||||
|
|
||||||
def _get_ids(self):
|
def _get_np(self):
|
||||||
"""Returns a generator producing a list of all queue IDs."""
|
"""Returns a generator producing a list of all queue (n, p)."""
|
||||||
cursor = self._col.find({}, fields={'_id': 1})
|
cursor = self._col.find({}, fields={'n': 1, 'p': 1})
|
||||||
return (doc['_id'] for doc in cursor)
|
return ((doc['n'], doc['p']) for doc in cursor)
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
#-----------------------------------------------------------------------
|
||||||
# Interface
|
# Interface
|
||||||
@@ -139,10 +139,10 @@ class QueueController(storage.QueueBase):
|
|||||||
|
|
||||||
@utils.raises_conn_error
|
@utils.raises_conn_error
|
||||||
def stats(self, name, project=None):
|
def stats(self, name, project=None):
|
||||||
queue_id = self._get_id(name, project)
|
self._get_id(name, project)
|
||||||
controller = self.driver.message_controller
|
controller = self.driver.message_controller
|
||||||
active = controller.active(queue_id)
|
active = controller.active(name, project=project)
|
||||||
claimed = controller.claimed(queue_id)
|
claimed = controller.claimed(name, project=project)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'actions': 0,
|
'actions': 0,
|
||||||
|
|||||||
@@ -92,28 +92,29 @@ class MessageController(base.MessageBase):
|
|||||||
|
|
||||||
with self.driver('deferred'):
|
with self.driver('deferred'):
|
||||||
sql = '''
|
sql = '''
|
||||||
select id, content, ttl, julianday() * 86400.0 - created
|
select M.id, content, ttl, julianday() * 86400.0 - created
|
||||||
from Messages
|
from Queues as Q join Messages as M
|
||||||
where ttl > julianday() * 86400.0 - created
|
on M.qid = Q.id
|
||||||
and qid = ?'''
|
where M.ttl > julianday() * 86400.0 - created
|
||||||
|
and Q.name = ? and Q.project = ?'''
|
||||||
|
|
||||||
args = [utils.get_qid(self.driver, queue, project)]
|
args = [queue, project]
|
||||||
|
|
||||||
if not echo:
|
if not echo:
|
||||||
sql += '''
|
sql += '''
|
||||||
and client != ?'''
|
and M.client != ?'''
|
||||||
args += [client_uuid]
|
args += [client_uuid]
|
||||||
|
|
||||||
if marker:
|
if marker:
|
||||||
sql += '''
|
sql += '''
|
||||||
and id > ?'''
|
and M.id > ?'''
|
||||||
args += [utils.marker_decode(marker)]
|
args += [utils.marker_decode(marker)]
|
||||||
|
|
||||||
if not include_claimed:
|
if not include_claimed:
|
||||||
sql += '''
|
sql += '''
|
||||||
and id not in (select msgid
|
and M.id not in (select msgid
|
||||||
from Claims join Locked
|
from Claims join Locked
|
||||||
on id = cid)'''
|
on id = cid)'''
|
||||||
|
|
||||||
sql += '''
|
sql += '''
|
||||||
limit ?'''
|
limit ?'''
|
||||||
@@ -146,8 +147,8 @@ class MessageController(base.MessageBase):
|
|||||||
|
|
||||||
self.driver.run('''
|
self.driver.run('''
|
||||||
delete from Messages
|
delete from Messages
|
||||||
where ttl <= julianday() * 86400.0 - created
|
where ttl <= julianday() * 86400.0 - created
|
||||||
and qid = ?''', qid)
|
and qid = ?''', qid)
|
||||||
|
|
||||||
# executemany() sets lastrowid to None, so no matter we manually
|
# executemany() sets lastrowid to None, so no matter we manually
|
||||||
# generate the IDs or not, we still need to query for it.
|
# generate the IDs or not, we still need to query for it.
|
||||||
|
|||||||
@@ -157,8 +157,7 @@ class MongodbMessageTests(base.MessageControllerTest):
|
|||||||
super(MongodbMessageTests, self).tearDown()
|
super(MongodbMessageTests, self).tearDown()
|
||||||
|
|
||||||
def _count_expired(self, queue, project=None):
|
def _count_expired(self, queue, project=None):
|
||||||
queue_id = self.queue_controller._get_id(queue, project)
|
return self.controller._count_expired(queue, project)
|
||||||
return self.controller._count_expired(queue_id)
|
|
||||||
|
|
||||||
def test_indexes(self):
|
def test_indexes(self):
|
||||||
col = self.controller._col
|
col = self.controller._col
|
||||||
@@ -172,16 +171,15 @@ class MongodbMessageTests(base.MessageControllerTest):
|
|||||||
iterations = 10
|
iterations = 10
|
||||||
|
|
||||||
self.queue_controller.create(queue_name)
|
self.queue_controller.create(queue_name)
|
||||||
queue_id = self.queue_controller._get_id(queue_name)
|
|
||||||
|
|
||||||
seed_marker1 = self.controller._next_marker(queue_name)
|
seed_marker1 = self.controller._next_marker(queue_name)
|
||||||
self.assertEqual(seed_marker1, 1, 'First marker is 1')
|
self.assertEqual(seed_marker1, 1, 'First marker is 1')
|
||||||
|
|
||||||
for i in range(iterations):
|
for i in range(iterations):
|
||||||
self.controller.post(queue_name, [{'ttl': 60}], 'uuid')
|
self.controller.post(queue_name, [{'ttl': 60}], 'uuid')
|
||||||
marker1 = self.controller._next_marker(queue_id)
|
marker1 = self.controller._next_marker(queue_name)
|
||||||
marker2 = self.controller._next_marker(queue_id)
|
marker2 = self.controller._next_marker(queue_name)
|
||||||
marker3 = self.controller._next_marker(queue_id)
|
marker3 = self.controller._next_marker(queue_name)
|
||||||
self.assertEqual(marker1, marker2)
|
self.assertEqual(marker1, marker2)
|
||||||
self.assertEqual(marker2, marker3)
|
self.assertEqual(marker2, marker3)
|
||||||
|
|
||||||
@@ -239,8 +237,7 @@ class MongodbMessageTests(base.MessageControllerTest):
|
|||||||
# Sanity-check that the most recent message is the
|
# Sanity-check that the most recent message is the
|
||||||
# one remaining in the queue.
|
# one remaining in the queue.
|
||||||
queue = random.choice(queue_names)
|
queue = random.choice(queue_names)
|
||||||
queue_id = self.queue_controller._get_id(queue, project)
|
message = self.driver.db.messages.find_one({'q': queue, 'p': project})
|
||||||
message = self.driver.db.messages.find_one({'q': queue_id})
|
|
||||||
self.assertEquals(message['k'], messages_per_queue)
|
self.assertEquals(message['k'], messages_per_queue)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ class MessagesBaseTest(base.TestBase):
|
|||||||
self.assertEquals(self.srmock.status, falcon.HTTP_204)
|
self.assertEquals(self.srmock.status, falcon.HTTP_204)
|
||||||
|
|
||||||
self.simulate_get(target, self.project_id, query_string=params)
|
self.simulate_get(target, self.project_id, query_string=params)
|
||||||
self.assertEquals(self.srmock.status, falcon.HTTP_404)
|
self.assertEquals(self.srmock.status, falcon.HTTP_204)
|
||||||
|
|
||||||
# Safe to delete non-existing ones
|
# Safe to delete non-existing ones
|
||||||
self.simulate_delete(target, self.project_id, query_string=params)
|
self.simulate_delete(target, self.project_id, query_string=params)
|
||||||
@@ -214,7 +214,7 @@ class MessagesBaseTest(base.TestBase):
|
|||||||
|
|
||||||
self.simulate_get('/v1/queues/nonexistent/messages', self.project_id,
|
self.simulate_get('/v1/queues/nonexistent/messages', self.project_id,
|
||||||
headers=self.headers)
|
headers=self.headers)
|
||||||
self.assertEquals(self.srmock.status, falcon.HTTP_404)
|
self.assertEquals(self.srmock.status, falcon.HTTP_204)
|
||||||
|
|
||||||
def test_list_with_bad_marker(self):
|
def test_list_with_bad_marker(self):
|
||||||
path = self.queue_path + '/messages'
|
path = self.queue_path + '/messages'
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class CollectionResource(object):
|
|||||||
# Prepare response
|
# Prepare response
|
||||||
messages = list(messages)
|
messages = list(messages)
|
||||||
if not messages:
|
if not messages:
|
||||||
raise falcon.HTTPNotFound()
|
return None
|
||||||
|
|
||||||
base_path += '/'
|
base_path += '/'
|
||||||
for each_message in messages:
|
for each_message in messages:
|
||||||
@@ -200,14 +200,14 @@ class CollectionResource(object):
|
|||||||
ids = req.get_param_as_list('ids')
|
ids = req.get_param_as_list('ids')
|
||||||
if ids is None:
|
if ids is None:
|
||||||
response = self._get(req, project_id, queue_name)
|
response = self._get(req, project_id, queue_name)
|
||||||
|
|
||||||
if response is None:
|
|
||||||
resp.status = falcon.HTTP_204
|
|
||||||
return
|
|
||||||
else:
|
else:
|
||||||
base_path = req.path + '/messages'
|
base_path = req.path + '/messages'
|
||||||
response = self._get_by_id(base_path, project_id, queue_name, ids)
|
response = self._get_by_id(base_path, project_id, queue_name, ids)
|
||||||
|
|
||||||
|
if response is None:
|
||||||
|
resp.status = falcon.HTTP_204
|
||||||
|
return
|
||||||
|
|
||||||
resp.body = helpers.to_json(response)
|
resp.body = helpers.to_json(response)
|
||||||
|
|
||||||
def on_delete(self, req, resp, project_id, queue_name):
|
def on_delete(self, req, resp, project_id, queue_name):
|
||||||
|
|||||||
Reference in New Issue
Block a user