Merge "container-server: do [end_]marker filtering in SQL query"
This commit is contained in:
commit
6c1d3535b0
@ -32,7 +32,7 @@ from swift.common.utils import Timestamp, encode_timestamps, \
|
|||||||
decode_timestamps, extract_swift_bytes, storage_directory, hash_path, \
|
decode_timestamps, extract_swift_bytes, storage_directory, hash_path, \
|
||||||
ShardRange, renamer, MD5_OF_EMPTY_STRING, mkdirs, get_db_files, \
|
ShardRange, renamer, MD5_OF_EMPTY_STRING, mkdirs, get_db_files, \
|
||||||
parse_db_filename, make_db_file_path, split_path, RESERVED_BYTE, \
|
parse_db_filename, make_db_file_path, split_path, RESERVED_BYTE, \
|
||||||
filter_namespaces, ShardRangeList
|
ShardRangeList, Namespace
|
||||||
from swift.common.db import DatabaseBroker, utf8encode, BROKER_TIMEOUT, \
|
from swift.common.db import DatabaseBroker, utf8encode, BROKER_TIMEOUT, \
|
||||||
zero_like, DatabaseAlreadyExists, SQLITE_ARG_LIMIT
|
zero_like, DatabaseAlreadyExists, SQLITE_ARG_LIMIT
|
||||||
|
|
||||||
@ -1685,7 +1685,8 @@ class ContainerBroker(DatabaseBroker):
|
|||||||
if ('no such table: %s' % SHARD_RANGE_TABLE) not in str(err):
|
if ('no such table: %s' % SHARD_RANGE_TABLE) not in str(err):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _get_shard_range_rows(self, connection=None, includes=None,
|
def _get_shard_range_rows(self, connection=None, marker=None,
|
||||||
|
end_marker=None, includes=None,
|
||||||
include_deleted=False, states=None,
|
include_deleted=False, states=None,
|
||||||
include_own=False, exclude_others=False):
|
include_own=False, exclude_others=False):
|
||||||
"""
|
"""
|
||||||
@ -1696,8 +1697,15 @@ class ContainerBroker(DatabaseBroker):
|
|||||||
``exclude_others=True``.
|
``exclude_others=True``.
|
||||||
|
|
||||||
:param connection: db connection
|
:param connection: db connection
|
||||||
|
:param marker: restricts the returned list to rows whose namespace
|
||||||
|
includes or is greater than the marker value. ``marker`` is ignored
|
||||||
|
if ``includes`` is specified.
|
||||||
|
:param end_marker: restricts the returned list to rows whose namespace
|
||||||
|
includes or is less than the end_marker value. ``end_marker`` is
|
||||||
|
ignored if ``includes`` is specified.
|
||||||
:param includes: restricts the returned list to the shard range that
|
:param includes: restricts the returned list to the shard range that
|
||||||
includes the given value
|
includes the given value; if ``includes`` is specified then
|
||||||
|
``marker`` and ``end_marker`` are ignored
|
||||||
:param include_deleted: include rows marked as deleted
|
:param include_deleted: include rows marked as deleted
|
||||||
:param states: include only rows matching the given state(s); can be an
|
:param states: include only rows matching the given state(s); can be an
|
||||||
int or a list of ints.
|
int or a list of ints.
|
||||||
@ -1741,7 +1749,14 @@ class ContainerBroker(DatabaseBroker):
|
|||||||
if exclude_others:
|
if exclude_others:
|
||||||
conditions.append('name = ?')
|
conditions.append('name = ?')
|
||||||
params.append(self.path)
|
params.append(self.path)
|
||||||
if includes is not None:
|
if includes is None:
|
||||||
|
if end_marker:
|
||||||
|
conditions.append('lower < ?')
|
||||||
|
params.append(end_marker)
|
||||||
|
if marker:
|
||||||
|
conditions.append("(upper = '' OR upper > ?)")
|
||||||
|
params.append(marker)
|
||||||
|
else:
|
||||||
conditions.extend(('lower < ?', "(upper = '' OR upper >= ?)"))
|
conditions.extend(('lower < ?', "(upper = '' OR upper >= ?)"))
|
||||||
params.extend((includes, includes))
|
params.extend((includes, includes))
|
||||||
if conditions:
|
if conditions:
|
||||||
@ -1826,8 +1841,10 @@ class ContainerBroker(DatabaseBroker):
|
|||||||
|
|
||||||
:param marker: restricts the returned list to shard ranges whose
|
:param marker: restricts the returned list to shard ranges whose
|
||||||
namespace includes or is greater than the marker value.
|
namespace includes or is greater than the marker value.
|
||||||
|
``marker`` is ignored if ``includes`` is specified.
|
||||||
:param end_marker: restricts the returned list to shard ranges whose
|
:param end_marker: restricts the returned list to shard ranges whose
|
||||||
namespace includes or is less than the end_marker value.
|
namespace includes or is less than the end_marker value.
|
||||||
|
``end_marker`` is ignored if ``includes`` is specified.
|
||||||
:param includes: restricts the returned list to the shard range that
|
:param includes: restricts the returned list to the shard range that
|
||||||
includes the given value; if ``includes`` is specified then
|
includes the given value; if ``includes`` is specified then
|
||||||
``marker`` and ``end_marker`` are ignored.
|
``marker`` and ``end_marker`` are ignored.
|
||||||
@ -1847,9 +1864,14 @@ class ContainerBroker(DatabaseBroker):
|
|||||||
:param fill_gaps: if True, insert a modified copy of own shard range to
|
:param fill_gaps: if True, insert a modified copy of own shard range to
|
||||||
fill any gap between the end of any found shard ranges and the
|
fill any gap between the end of any found shard ranges and the
|
||||||
upper bound of own shard range. Gaps enclosed within the found
|
upper bound of own shard range. Gaps enclosed within the found
|
||||||
shard ranges are not filled.
|
shard ranges are not filled. ``fill_gaps`` is ignored if
|
||||||
|
``includes`` is specified.
|
||||||
:return: a list of instances of :class:`swift.common.utils.ShardRange`
|
:return: a list of instances of :class:`swift.common.utils.ShardRange`
|
||||||
"""
|
"""
|
||||||
|
if includes is None and (marker == Namespace.MAX
|
||||||
|
or end_marker == Namespace.MIN):
|
||||||
|
return []
|
||||||
|
|
||||||
if reverse:
|
if reverse:
|
||||||
marker, end_marker = end_marker, marker
|
marker, end_marker = end_marker, marker
|
||||||
if marker and end_marker and marker >= end_marker:
|
if marker and end_marker and marker >= end_marker:
|
||||||
@ -1858,17 +1880,13 @@ class ContainerBroker(DatabaseBroker):
|
|||||||
shard_ranges = [
|
shard_ranges = [
|
||||||
ShardRange(*row)
|
ShardRange(*row)
|
||||||
for row in self._get_shard_range_rows(
|
for row in self._get_shard_range_rows(
|
||||||
includes=includes, include_deleted=include_deleted,
|
marker=marker, end_marker=end_marker, includes=includes,
|
||||||
states=states, include_own=include_own,
|
include_deleted=include_deleted, states=states,
|
||||||
exclude_others=exclude_others)]
|
include_own=include_own, exclude_others=exclude_others)]
|
||||||
|
|
||||||
shard_ranges.sort(key=ShardRange.sort_key)
|
shard_ranges.sort(key=ShardRange.sort_key)
|
||||||
if includes:
|
if includes:
|
||||||
return shard_ranges[:1] if shard_ranges else []
|
return shard_ranges[:1] if shard_ranges else []
|
||||||
|
|
||||||
shard_ranges = filter_namespaces(
|
|
||||||
shard_ranges, includes, marker, end_marker)
|
|
||||||
|
|
||||||
if fill_gaps:
|
if fill_gaps:
|
||||||
own_shard_range = self.get_own_shard_range()
|
own_shard_range = self.get_own_shard_range()
|
||||||
if shard_ranges:
|
if shard_ranges:
|
||||||
|
@ -41,7 +41,7 @@ from swift.common.db import DatabaseAlreadyExists, GreenDBConnection, \
|
|||||||
TombstoneReclaimer, GreenDBCursor
|
TombstoneReclaimer, GreenDBCursor
|
||||||
from swift.common.request_helpers import get_reserved_name
|
from swift.common.request_helpers import get_reserved_name
|
||||||
from swift.common.utils import Timestamp, encode_timestamps, hash_path, \
|
from swift.common.utils import Timestamp, encode_timestamps, hash_path, \
|
||||||
ShardRange, make_db_file_path, md5, ShardRangeList
|
ShardRange, make_db_file_path, md5, ShardRangeList, Namespace
|
||||||
from swift.common.storage_policy import POLICIES
|
from swift.common.storage_policy import POLICIES
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
@ -4296,6 +4296,76 @@ class TestContainerBroker(test_db.TestDbBase):
|
|||||||
actual = broker.get_shard_ranges(marker='e', end_marker='e')
|
actual = broker.get_shard_ranges(marker='e', end_marker='e')
|
||||||
self.assertFalse([dict(sr) for sr in actual])
|
self.assertFalse([dict(sr) for sr in actual])
|
||||||
|
|
||||||
|
# includes overrides include_own
|
||||||
|
actual = broker.get_shard_ranges(includes='b', include_own=True)
|
||||||
|
self.assertEqual([dict(shard_ranges[0])], [dict(sr) for sr in actual])
|
||||||
|
# ... unless they coincide
|
||||||
|
actual = broker.get_shard_ranges(includes='t', include_own=True)
|
||||||
|
self.assertEqual([dict(own_shard_range)], [dict(sr) for sr in actual])
|
||||||
|
|
||||||
|
# exclude_others overrides includes
|
||||||
|
actual = broker.get_shard_ranges(includes='b', exclude_others=True)
|
||||||
|
self.assertFalse(actual)
|
||||||
|
|
||||||
|
# include_deleted overrides includes
|
||||||
|
actual = broker.get_shard_ranges(includes='i', include_deleted=True)
|
||||||
|
self.assertEqual([dict(shard_ranges[-1])], [dict(sr) for sr in actual])
|
||||||
|
actual = broker.get_shard_ranges(includes='i', include_deleted=False)
|
||||||
|
self.assertFalse(actual)
|
||||||
|
|
||||||
|
# includes overrides marker/end_marker
|
||||||
|
actual = broker.get_shard_ranges(includes='b', marker='e',
|
||||||
|
end_marker='')
|
||||||
|
self.assertEqual([dict(shard_ranges[0])], [dict(sr) for sr in actual])
|
||||||
|
|
||||||
|
actual = broker.get_shard_ranges(includes='b', marker=Namespace.MAX)
|
||||||
|
self.assertEqual([dict(shard_ranges[0])], [dict(sr) for sr in actual])
|
||||||
|
|
||||||
|
# end_marker is Namespace.MAX
|
||||||
|
actual = broker.get_shard_ranges(marker='e', end_marker='')
|
||||||
|
self.assertEqual([dict(sr) for sr in undeleted[2:]],
|
||||||
|
[dict(sr) for sr in actual])
|
||||||
|
|
||||||
|
actual = broker.get_shard_ranges(marker='e', end_marker='',
|
||||||
|
reverse=True)
|
||||||
|
self.assertEqual([dict(sr) for sr in reversed(undeleted[:3])],
|
||||||
|
[dict(sr) for sr in actual])
|
||||||
|
|
||||||
|
# marker is Namespace.MIN
|
||||||
|
actual = broker.get_shard_ranges(marker='', end_marker='d')
|
||||||
|
self.assertEqual([dict(sr) for sr in shard_ranges[:2]],
|
||||||
|
[dict(sr) for sr in actual])
|
||||||
|
|
||||||
|
actual = broker.get_shard_ranges(marker='', end_marker='d',
|
||||||
|
reverse=True, include_deleted=True)
|
||||||
|
self.assertEqual([dict(sr) for sr in reversed(shard_ranges[2:])],
|
||||||
|
[dict(sr) for sr in actual])
|
||||||
|
|
||||||
|
# marker, end_marker span entire namespace
|
||||||
|
actual = broker.get_shard_ranges(marker='', end_marker='')
|
||||||
|
self.assertEqual([dict(sr) for sr in undeleted],
|
||||||
|
[dict(sr) for sr in actual])
|
||||||
|
|
||||||
|
# marker, end_marker override include_own
|
||||||
|
actual = broker.get_shard_ranges(marker='', end_marker='k',
|
||||||
|
include_own=True)
|
||||||
|
self.assertEqual([dict(sr) for sr in undeleted],
|
||||||
|
[dict(sr) for sr in actual])
|
||||||
|
actual = broker.get_shard_ranges(marker='u', end_marker='',
|
||||||
|
include_own=True)
|
||||||
|
self.assertFalse(actual)
|
||||||
|
# ...unless they coincide
|
||||||
|
actual = broker.get_shard_ranges(marker='t', end_marker='',
|
||||||
|
include_own=True)
|
||||||
|
self.assertEqual([dict(own_shard_range)], [dict(sr) for sr in actual])
|
||||||
|
|
||||||
|
# null namespace cases
|
||||||
|
actual = broker.get_shard_ranges(end_marker=Namespace.MIN)
|
||||||
|
self.assertFalse(actual)
|
||||||
|
|
||||||
|
actual = broker.get_shard_ranges(marker=Namespace.MAX)
|
||||||
|
self.assertFalse(actual)
|
||||||
|
|
||||||
orig_execute = GreenDBConnection.execute
|
orig_execute = GreenDBConnection.execute
|
||||||
mock_call_args = []
|
mock_call_args = []
|
||||||
|
|
||||||
@ -4312,6 +4382,19 @@ class TestContainerBroker(test_db.TestDbBase):
|
|||||||
# verify that includes keyword plumbs through to an SQL condition
|
# verify that includes keyword plumbs through to an SQL condition
|
||||||
self.assertIn("WHERE deleted=0 AND name != ? AND lower < ? AND "
|
self.assertIn("WHERE deleted=0 AND name != ? AND lower < ? AND "
|
||||||
"(upper = '' OR upper >= ?)", mock_call_args[0][1])
|
"(upper = '' OR upper >= ?)", mock_call_args[0][1])
|
||||||
|
self.assertEqual(['a/c', 'f', 'f'], mock_call_args[0][2])
|
||||||
|
|
||||||
|
mock_call_args = []
|
||||||
|
with mock.patch('swift.common.db.GreenDBConnection.execute',
|
||||||
|
mock_execute):
|
||||||
|
actual = broker.get_shard_ranges(marker='c', end_marker='d')
|
||||||
|
self.assertEqual([dict(sr) for sr in shard_ranges[1:2]],
|
||||||
|
[dict(sr) for sr in actual])
|
||||||
|
self.assertEqual(1, len(mock_call_args))
|
||||||
|
# verify that marker & end_marker plumb through to an SQL condition
|
||||||
|
self.assertIn("WHERE deleted=0 AND name != ? AND lower < ? AND "
|
||||||
|
"(upper = '' OR upper > ?)", mock_call_args[0][1])
|
||||||
|
self.assertEqual(['a/c', 'd', 'c'], mock_call_args[0][2])
|
||||||
|
|
||||||
actual = broker.get_shard_ranges(includes='i')
|
actual = broker.get_shard_ranges(includes='i')
|
||||||
self.assertFalse(actual)
|
self.assertFalse(actual)
|
||||||
@ -4338,6 +4421,10 @@ class TestContainerBroker(test_db.TestDbBase):
|
|||||||
filler.upper = 'k'
|
filler.upper = 'k'
|
||||||
self.assertEqual([dict(sr) for sr in undeleted + [filler]],
|
self.assertEqual([dict(sr) for sr in undeleted + [filler]],
|
||||||
[dict(sr) for sr in actual])
|
[dict(sr) for sr in actual])
|
||||||
|
# includes overrides fill_gaps
|
||||||
|
actual = broker.get_shard_ranges(includes='b', fill_gaps=True)
|
||||||
|
self.assertEqual([dict(shard_ranges[0])], [dict(sr) for sr in actual])
|
||||||
|
|
||||||
# no filler needed...
|
# no filler needed...
|
||||||
actual = broker.get_shard_ranges(fill_gaps=True, end_marker='h')
|
actual = broker.get_shard_ranges(fill_gaps=True, end_marker='h')
|
||||||
self.assertEqual([dict(sr) for sr in undeleted],
|
self.assertEqual([dict(sr) for sr in undeleted],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user