feat(storage): configurable default paging size

This change add the following options to the config file:

    [limits:storage]
    default_queue_paging = 10
    default_message_paging = 10

So that the default value of the  "limit" URI param is now configurable.

This patch also removes the "actions" cruft.

Implements: blueprint configurable-default-paging
Change-Id: Id38295f1e607226a4259be7744e6ce2d7b6de12e
This commit is contained in:
Zhihao Yuan 2013-08-20 11:44:08 -04:00 committed by kgriffs
parent 0a03263154
commit 9e5754695d
11 changed files with 156 additions and 32 deletions

View File

@ -84,3 +84,9 @@ database = marconi
# for each metadata body and each message body # for each metadata body and each message body
;metadata_size_uplimit = 65536 ;metadata_size_uplimit = 65536
;message_size_uplimit = 262144 ;message_size_uplimit = 262144
[limits:storage]
# The default number of queue records per page when listing queues
;default_queue_paging = 10
# The default number of messages per page when listing or claiming messages
;default_message_paging = 10

View File

@ -90,7 +90,8 @@ class QueueBase(ControllerBase):
:param project: Project id :param project: Project id
:param marker: The last queue name :param marker: The last queue name
:param limit: (Default 10) Max number :param limit: (Default 10, configurable) Max number
queues to return.
:param detailed: Whether metadata is included :param detailed: Whether metadata is included
:param include_claimed: Whether to list claimed messages :param include_claimed: Whether to list claimed messages
@ -164,18 +165,6 @@ class QueueBase(ControllerBase):
""" """
raise NotImplementedError raise NotImplementedError
@abc.abstractmethod
def actions(self, name, project=None, marker=None, limit=10):
"""Base method for queue actions.
:param name: Queue name
:param project: Project id
:param marker: Tail identifier
:param limit: (Default 10) Max number
of messages to retrieve.
"""
raise NotImplementedError
class MessageBase(ControllerBase): class MessageBase(ControllerBase):
"""This class is responsible for managing message CRUD.""" """This class is responsible for managing message CRUD."""
@ -191,7 +180,7 @@ class MessageBase(ControllerBase):
message from. message from.
:param project: Project id :param project: Project id
:param marker: Tail identifier :param marker: Tail identifier
:param limit: (Default 10) specifies up to 100 :param limit: (Default 10, configurable) Max number
messages to return. messages to return.
:param echo: (Default False) Boolean expressing whether :param echo: (Default False) Boolean expressing whether
or not this client should receive its own messages. or not this client should receive its own messages.
@ -302,7 +291,7 @@ class ClaimBase(ControllerBase):
:param metadata: Claim's parameters :param metadata: Claim's parameters
to be stored. to be stored.
:param project: Project id :param project: Project id
:param limit: (Default 10) Max number :param limit: (Default 10, configurable) Max number
of messages to claim. of messages to claim.
:returns: (Claim ID, claimed messages) :returns: (Claim ID, claimed messages)

View File

@ -25,14 +25,17 @@ import datetime
from bson import objectid from bson import objectid
from marconi.common import config
import marconi.openstack.common.log as logging import marconi.openstack.common.log as logging
from marconi.openstack.common import timeutils from marconi.openstack.common import timeutils
from marconi import storage from marconi import storage
from marconi.storage import exceptions from marconi.storage import exceptions
from marconi.storage.mongodb import utils from marconi.storage.mongodb import utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
CFG = config.namespace('limits:storage').from_options(
default_message_paging=10,
)
class ClaimController(storage.ClaimBase): class ClaimController(storage.ClaimBase):
@ -98,7 +101,7 @@ class ClaimController(storage.ClaimBase):
return (claim, msgs) 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=None):
"""Creates a claim. """Creates a claim.
This implementation was done in a best-effort fashion. This implementation was done in a best-effort fashion.
@ -118,6 +121,9 @@ class ClaimController(storage.ClaimBase):
""" """
msg_ctrl = self.driver.message_controller msg_ctrl = self.driver.message_controller
if limit is None:
limit = CFG.default_message_paging
ttl = metadata['ttl'] ttl = metadata['ttl']
grace = metadata['grace'] grace = metadata['grace']
oid = objectid.ObjectId() oid = objectid.ObjectId()

View File

@ -26,6 +26,7 @@ import time
import pymongo.errors import pymongo.errors
from marconi.common import config
import marconi.openstack.common.log as logging import marconi.openstack.common.log as logging
from marconi.openstack.common import timeutils from marconi.openstack.common import timeutils
from marconi import storage from marconi import storage
@ -34,6 +35,9 @@ from marconi.storage.mongodb import options
from marconi.storage.mongodb import utils from marconi.storage.mongodb import utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
CFG = config.namespace('limits:storage').from_options(
default_message_paging=10,
)
class MessageController(storage.MessageBase): class MessageController(storage.MessageBase):
@ -397,9 +401,12 @@ class MessageController(storage.MessageBase):
for name, project in self._queue_controller._get_np(): for name, project in self._queue_controller._get_np():
self._remove_expired(name, project) self._remove_expired(name, project)
def list(self, queue_name, project=None, marker=None, limit=10, def list(self, queue_name, project=None, marker=None, limit=None,
echo=False, client_uuid=None, include_claimed=False): echo=False, client_uuid=None, include_claimed=False):
if limit is None:
limit = CFG.default_message_paging
if marker is not None: if marker is not None:
try: try:
marker = int(marker) marker = int(marker)

View File

@ -23,6 +23,7 @@ Field Mappings:
import pymongo.errors import pymongo.errors
from marconi.common import config
import marconi.openstack.common.log as logging import marconi.openstack.common.log as logging
from marconi.openstack.common import timeutils from marconi.openstack.common import timeutils
from marconi import storage from marconi import storage
@ -30,6 +31,9 @@ from marconi.storage import exceptions
from marconi.storage.mongodb import utils from marconi.storage.mongodb import utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
CFG = config.namespace('limits:storage').from_options(
default_queue_paging=10,
)
class QueueController(storage.QueueBase): class QueueController(storage.QueueBase):
@ -77,7 +81,11 @@ class QueueController(storage.QueueBase):
#----------------------------------------------------------------------- #-----------------------------------------------------------------------
def list(self, project=None, marker=None, def list(self, project=None, marker=None,
limit=10, detailed=False): limit=None, detailed=False):
if limit is None:
limit = CFG.default_queue_paging
query = {'p': project} query = {'p': project}
if marker: if marker:
query['n'] = {'$gt': marker} query['n'] = {'$gt': marker}
@ -161,7 +169,3 @@ class QueueController(storage.QueueBase):
message_stats['newest'] = utils.stat_message(newest, now) message_stats['newest'] = utils.stat_message(newest, now)
return {'messages': message_stats} return {'messages': message_stats}
@utils.raises_conn_error
def actions(self, name, project=None, marker=None, limit=10):
raise NotImplementedError

View File

@ -13,10 +13,15 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from marconi.common import config
from marconi.storage import base from marconi.storage import base
from marconi.storage import exceptions from marconi.storage import exceptions
from marconi.storage.sqlite import utils from marconi.storage.sqlite import utils
CFG = config.namespace('limits:storage').from_options(
default_message_paging=10,
)
class ClaimController(base.ClaimBase): class ClaimController(base.ClaimBase):
def __init__(self, driver): def __init__(self, driver):
@ -73,10 +78,14 @@ class ClaimController(base.ClaimBase):
except utils.NoResult: except utils.NoResult:
raise exceptions.ClaimDoesNotExist(claim_id, queue, project) raise exceptions.ClaimDoesNotExist(claim_id, queue, project)
def create(self, queue, metadata, project, limit=10): def create(self, queue, metadata, project, limit=None):
if project is None: if project is None:
project = '' project = ''
if limit is None:
limit = CFG.default_message_paging
with self.driver('immediate'): with self.driver('immediate'):
try: try:
qid = utils.get_qid(self.driver, queue, project) qid = utils.get_qid(self.driver, queue, project)

View File

@ -13,11 +13,16 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from marconi.common import config
from marconi.openstack.common import timeutils from marconi.openstack.common import timeutils
from marconi.storage import base from marconi.storage import base
from marconi.storage import exceptions from marconi.storage import exceptions
from marconi.storage.sqlite import utils from marconi.storage.sqlite import utils
CFG = config.namespace('limits:storage').from_options(
default_message_paging=10,
)
class MessageController(base.MessageBase): class MessageController(base.MessageBase):
def __init__(self, driver): def __init__(self, driver):
@ -130,9 +135,12 @@ class MessageController(base.MessageBase):
'body': content, 'body': content,
} }
def list(self, queue, project, marker=None, limit=10, def list(self, queue, project, marker=None, limit=None,
echo=False, client_uuid=None, include_claimed=False): echo=False, client_uuid=None, include_claimed=False):
if limit is None:
limit = CFG.default_message_paging
if project is None: if project is None:
project = '' project = ''

View File

@ -14,10 +14,15 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from marconi.common import config
from marconi.storage import base from marconi.storage import base
from marconi.storage import exceptions from marconi.storage import exceptions
from marconi.storage.sqlite import utils from marconi.storage.sqlite import utils
CFG = config.namespace('limits:storage').from_options(
default_queue_paging=10,
)
class QueueController(base.QueueBase): class QueueController(base.QueueBase):
def __init__(self, driver): def __init__(self, driver):
@ -36,11 +41,14 @@ class QueueController(base.QueueBase):
''') ''')
def list(self, project, marker=None, def list(self, project, marker=None,
limit=10, detailed=False): limit=None, detailed=False):
if project is None: if project is None:
project = '' project = ''
if limit is None:
limit = CFG.default_queue_paging
sql = ((''' sql = (('''
select name from Queues''' if not detailed select name from Queues''' if not detailed
else ''' else '''
@ -166,6 +174,3 @@ class QueueController(base.QueueBase):
message_stats['newest'] = utils.stat_message(newest) message_stats['newest'] = utils.stat_message(newest)
return {'messages': message_stats} return {'messages': message_stats}
def actions(self, name, project, marker=None, limit=10):
raise NotImplementedError

View File

@ -0,0 +1,7 @@
[drivers]
transport = wsgi
storage = sqlite
[limits:storage]
default_queue_paging = 1
default_message_paging = 2

View File

@ -0,0 +1,86 @@
# Copyright (c) 2013 Rackspace, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import falcon
from marconi.tests.transport.wsgi import base
class DefaultLimitsTest(base.TestBase):
config_filename = 'wsgi_sqlite_default_limits.conf'
def setUp(self):
super(DefaultLimitsTest, self).setUp()
self.queue_path = '/v1/queues/q1'
self.messages_path = self.queue_path + '/messages'
self.claims_path = self.queue_path + '/claims'
self.simulate_put(self.queue_path)
def tearDown(self):
self.simulate_delete(self.queue_path)
super(DefaultLimitsTest, self).tearDown()
def test_queue_listing(self):
default_queue_paging = 1
# 2 queues to list
self.simulate_put('/v1/queues/q2')
self.assertEquals(self.srmock.status, falcon.HTTP_201)
result = self.simulate_get('/v1/queues')
self.assertEquals(self.srmock.status, falcon.HTTP_200)
queues = json.loads(result[0])['queues']
self.assertEquals(len(queues), default_queue_paging)
self.simulate_delete('/v1/queues/q2')
def test_message_listing(self):
default_message_paging = 2
# 10 messages to list
self.__prepare_messages(10)
result = self.simulate_get(self.messages_path,
headers={'Client-ID': 'audience'})
self.assertEquals(self.srmock.status, falcon.HTTP_200)
messages = json.loads(result[0])['messages']
self.assertEquals(len(messages), default_message_paging)
def test_claim_creation(self):
default_message_paging = 2
# 5 messages to claim
self.__prepare_messages(5)
result = self.simulate_post(self.claims_path,
body='{"ttl": 60, "grace": 60}')
self.assertEquals(self.srmock.status, falcon.HTTP_201)
messages = json.loads(result[0])
self.assertEquals(len(messages), default_message_paging)
def __prepare_messages(self, count):
doc = json.dumps([{'body': 239, 'ttl': 300}] * count)
self.simulate_post(self.messages_path, body=doc,
headers={'Client-ID': 'poster'})

View File

@ -56,9 +56,6 @@ class QueueController(storage.QueueBase):
def stats(self, name, project=None): def stats(self, name, project=None):
raise NotImplementedError() raise NotImplementedError()
def actions(self, name, project=None, marker=None, limit=10):
raise NotImplementedError()
class MessageController(storage.MessageBase): class MessageController(storage.MessageBase):
def __init__(self, driver): def __init__(self, driver):