Merge "Allow running db archiving continuously"
This commit is contained in:
commit
870a77f571
@ -825,28 +825,53 @@ class DbCommands(object):
|
|||||||
"""Print the current database version."""
|
"""Print the current database version."""
|
||||||
print(migration.db_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')
|
help='Maximum number of deleted rows to archive')
|
||||||
@args('--verbose', action='store_true', dest='verbose', default=False,
|
@args('--verbose', action='store_true', dest='verbose', default=False,
|
||||||
help='Print how many rows were archived per table.')
|
help='Print how many rows were archived per table.')
|
||||||
def archive_deleted_rows(self, max_rows, verbose=False):
|
@args('--until-complete', action='store_true', dest='until_complete',
|
||||||
"""Move up to max_rows deleted rows from production tables to shadow
|
default=False,
|
||||||
tables.
|
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
|
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
|
archived, 2 if max_rows is invalid. If automating, this should be
|
||||||
run continuously while the result is 1, stopping at 0.
|
run continuously while the result is 1, stopping at 0.
|
||||||
"""
|
"""
|
||||||
if max_rows is not None:
|
max_rows = int(max_rows)
|
||||||
max_rows = int(max_rows)
|
if max_rows < 0:
|
||||||
if max_rows < 0:
|
print(_("Must supply a positive value for max_rows"))
|
||||||
print(_("Must supply a positive value for max_rows"))
|
return(2)
|
||||||
return(2)
|
if max_rows > db.MAX_INT:
|
||||||
if max_rows > db.MAX_INT:
|
print(_('max rows must be <= %(max_value)d') %
|
||||||
print(_('max rows must be <= %(max_value)d') %
|
{'max_value': db.MAX_INT})
|
||||||
{'max_value': db.MAX_INT})
|
return(2)
|
||||||
return(2)
|
|
||||||
table_to_rows_archived = db.archive_deleted_rows(max_rows)
|
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 verbose:
|
||||||
if table_to_rows_archived:
|
if table_to_rows_archived:
|
||||||
utils.print_dict(table_to_rows_archived, _('Table'),
|
utils.print_dict(table_to_rows_archived, _('Table'),
|
||||||
|
@ -503,6 +503,70 @@ class DBCommandsTestCase(test.NoDBTestCase):
|
|||||||
# Tests that we get table output.
|
# Tests that we get table output.
|
||||||
self._test_archive_deleted_rows(verbose=True)
|
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={})
|
@mock.patch.object(db, 'archive_deleted_rows', return_value={})
|
||||||
def test_archive_deleted_rows_verbose_no_results(self, mock_db_archive):
|
def test_archive_deleted_rows_verbose_no_results(self, mock_db_archive):
|
||||||
result = self.commands.archive_deleted_rows(20, verbose=True)
|
result = self.commands.archive_deleted_rows(20, verbose=True)
|
||||||
|
7
releasenotes/notes/archive-all-db-aadf2ce0394c24fa.yaml
Normal file
7
releasenotes/notes/archive-all-db-aadf2ce0394c24fa.yaml
Normal 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.
|
Loading…
x
Reference in New Issue
Block a user