Merge "glance-manage db purge failure for limit"

This commit is contained in:
Jenkins 2016-03-15 20:50:40 +00:00 committed by Gerrit Code Review
commit c5dcf9021a
2 changed files with 77 additions and 10 deletions

View File

@ -34,8 +34,10 @@ import six
# NOTE(jokke): simplified transition to py3, behaves like py2 xrange # NOTE(jokke): simplified transition to py3, behaves like py2 xrange
from six.moves import range from six.moves import range
import sqlalchemy import sqlalchemy
from sqlalchemy import MetaData, Table, select from sqlalchemy.ext.compiler import compiles
from sqlalchemy import MetaData, Table
import sqlalchemy.orm as sa_orm import sqlalchemy.orm as sa_orm
from sqlalchemy import sql
import sqlalchemy.sql as sa_sql import sqlalchemy.sql as sa_sql
from glance.common import exception from glance.common import exception
@ -1247,6 +1249,24 @@ def image_tag_get_all(context, image_id, session=None):
return [tag[0] for tag in tags] return [tag[0] for tag in tags]
class DeleteFromSelect(sa_sql.expression.UpdateBase):
def __init__(self, table, select, column):
self.table = table
self.select = select
self.column = column
# NOTE(abhishekk): MySQL doesn't yet support subquery with
# 'LIMIT & IN/ALL/ANY/SOME' We need work around this with nesting select.
@compiles(DeleteFromSelect)
def visit_delete_from_select(element, compiler, **kw):
return "DELETE FROM %s WHERE %s in (SELECT T1.%s FROM (%s) as T1)" % (
compiler.process(element.table, asfrom=True),
compiler.process(element.column),
element.column.name,
compiler.process(element.select))
def purge_deleted_rows(context, age_in_days, max_rows, session=None): def purge_deleted_rows(context, age_in_days, max_rows, session=None):
"""Purges soft deleted rows """Purges soft deleted rows
@ -1294,16 +1314,19 @@ def purge_deleted_rows(context, age_in_days, max_rows, session=None):
_LI('Purging deleted rows older than %(age_in_days)d day(s) ' _LI('Purging deleted rows older than %(age_in_days)d day(s) '
'from table %(tbl)s'), 'from table %(tbl)s'),
{'age_in_days': age_in_days, 'tbl': tbl}) {'age_in_days': age_in_days, 'tbl': tbl})
column = tab.c.id
deleted_at_column = tab.c.deleted_at
query_delete = sql.select(
[column], deleted_at_column < deleted_age).order_by(
deleted_at_column).limit(max_rows)
delete_statement = DeleteFromSelect(tab, query_delete, column)
with session.begin(): with session.begin():
result = session.execute( result = session.execute(delete_statement)
tab.delete().where(
tab.columns.id.in_(
select([tab.columns.id]).where(
tab.columns.deleted_at < deleted_age
).limit(max_rows)
)
)
)
rows = result.rowcount rows = result.rowcount
LOG.info(_LI('Deleted %(rows)d row(s) from table %(tbl)s'), LOG.info(_LI('Deleted %(rows)d row(s) from table %(tbl)s'),
{'rows': rows, 'tbl': tbl}) {'rows': rows, 'tbl': tbl})

View File

@ -0,0 +1,44 @@
# Copyright 2016 OpenStack Foundation.
# Copyright 2016 NTT Data.
# All Rights Reserved.
#
# 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 mock
from glance.cmd import manage
from glance import context
from glance.db.sqlalchemy import api as db_api
import glance.tests.utils as test_utils
TENANT1 = '6838eb7b-6ded-434a-882c-b344c77fe8df'
USER1 = '54492ba0-f4df-4e4e-be62-27f4d76b29cf'
class DBCommandsTestCase(test_utils.BaseTestCase):
def setUp(self):
super(DBCommandsTestCase, self).setUp()
self.commands = manage.DbCommands()
self.context = context.RequestContext(
user=USER1, tenant=TENANT1)
@mock.patch.object(db_api, 'purge_deleted_rows')
@mock.patch.object(context, 'get_admin_context')
def test_purge_command(self, mock_context, mock_db_purge):
mock_context.return_value = self.context
self.commands.purge(1, 100)
mock_db_purge.assert_called_once_with(self.context, 1, 100)
def test_purge_command_negative_rows(self):
exit = self.assertRaises(SystemExit, self.commands.purge, 1, -1)
self.assertEqual("Minimal rows limit is 1.", exit.code)