Merge "Allow running db archiving continuously"

This commit is contained in:
Jenkins 2016-10-05 00:56:53 +00:00 committed by Gerrit Code Review
commit 870a77f571
3 changed files with 110 additions and 14 deletions

View File

@ -825,28 +825,53 @@ class DbCommands(object):
"""Print the current database version."""
print(migration.db_version())
@args('--max_rows', metavar='<number>',
@args('--max_rows', metavar='<number>', default=1000,
help='Maximum number of deleted rows to archive')
@args('--verbose', action='store_true', dest='verbose', default=False,
help='Print how many rows were archived per table.')
def archive_deleted_rows(self, max_rows, verbose=False):
"""Move up to max_rows deleted rows from production tables to shadow
tables.
@args('--until-complete', action='store_true', dest='until_complete',
default=False,
help=('Run continuously until all deleted rows are archived. Use '
'max_rows as a batch size for each iteration.'))
def archive_deleted_rows(self, max_rows, verbose=False,
until_complete=False):
"""Move deleted rows from production tables to shadow tables.
Returns 0 if nothing was archived, 1 if some number of rows were
archived, 2 if max_rows is invalid. If automating, this should be
run continuously while the result is 1, stopping at 0.
"""
if max_rows is not None:
max_rows = int(max_rows)
if max_rows < 0:
print(_("Must supply a positive value for max_rows"))
return(2)
if max_rows > db.MAX_INT:
print(_('max rows must be <= %(max_value)d') %
{'max_value': db.MAX_INT})
return(2)
table_to_rows_archived = db.archive_deleted_rows(max_rows)
max_rows = int(max_rows)
if max_rows < 0:
print(_("Must supply a positive value for max_rows"))
return(2)
if max_rows > db.MAX_INT:
print(_('max rows must be <= %(max_value)d') %
{'max_value': db.MAX_INT})
return(2)
table_to_rows_archived = {}
if until_complete and verbose:
sys.stdout.write(_('Archiving') + '..') # noqa
while True:
try:
run = db.archive_deleted_rows(max_rows)
except KeyboardInterrupt:
run = {}
if until_complete and verbose:
print('.' + _('stopped')) # noqa
break
for k, v in run.items():
table_to_rows_archived.setdefault(k, 0)
table_to_rows_archived[k] += v
if not until_complete:
break
elif not run:
if verbose:
print('.' + _('complete')) # noqa
break
if verbose:
sys.stdout.write('.')
if verbose:
if table_to_rows_archived:
utils.print_dict(table_to_rows_archived, _('Table'),

View File

@ -503,6 +503,70 @@ class DBCommandsTestCase(test.NoDBTestCase):
# Tests that we get table output.
self._test_archive_deleted_rows(verbose=True)
@mock.patch.object(db, 'archive_deleted_rows')
def test_archive_deleted_rows_until_complete(self, mock_db_archive,
verbose=False):
mock_db_archive.side_effect = [
{'instances': 10, 'instance_extra': 5},
{'instances': 5, 'instance_faults': 1},
{}]
result = self.commands.archive_deleted_rows(20, verbose=verbose,
until_complete=True)
self.assertEqual(1, result)
if verbose:
expected = """\
Archiving.....complete
+-----------------+-------------------------+
| Table | Number of Rows Archived |
+-----------------+-------------------------+
| instance_extra | 5 |
| instance_faults | 1 |
| instances | 15 |
+-----------------+-------------------------+
"""
else:
expected = ''
self.assertEqual(expected, self.output.getvalue())
mock_db_archive.assert_has_calls([mock.call(20),
mock.call(20),
mock.call(20)])
def test_archive_deleted_rows_until_complete_quiet(self):
self.test_archive_deleted_rows_until_complete(verbose=False)
@mock.patch.object(db, 'archive_deleted_rows')
def test_archive_deleted_rows_until_stopped(self, mock_db_archive,
verbose=True):
mock_db_archive.side_effect = [
{'instances': 10, 'instance_extra': 5},
{'instances': 5, 'instance_faults': 1},
KeyboardInterrupt]
result = self.commands.archive_deleted_rows(20, verbose=verbose,
until_complete=True)
self.assertEqual(1, result)
if verbose:
expected = """\
Archiving.....stopped
+-----------------+-------------------------+
| Table | Number of Rows Archived |
+-----------------+-------------------------+
| instance_extra | 5 |
| instance_faults | 1 |
| instances | 15 |
+-----------------+-------------------------+
"""
else:
expected = ''
self.assertEqual(expected, self.output.getvalue())
mock_db_archive.assert_has_calls([mock.call(20),
mock.call(20),
mock.call(20)])
def test_archive_deleted_rows_until_stopped_quiet(self):
self.test_archive_deleted_rows_until_stopped(verbose=False)
@mock.patch.object(db, 'archive_deleted_rows', return_value={})
def test_archive_deleted_rows_verbose_no_results(self, mock_db_archive):
result = self.commands.archive_deleted_rows(20, verbose=True)

View File

@ -0,0 +1,7 @@
---
features:
- |
Support for archiving all deleted rows from the database has
been added to the ``nova-manage db archive_deleted_rows``
command. The ``--until-complete`` option will continuously
run the process until no more rows are available for archiving.