Use decimal type for start_time and end_time

* Change start_time and end_time type in action model
* Add db migration script for start_time and end_time type change

Using float type for start_time and end_time caused inaccurate
timestamps to be stored [1].  This change uses decimal type for
start_time and end_time.

[1] https://dev.mysql.com/doc/refman/5.7/en/problems-with-float.html

Change-Id: Ia6c1f0a0379695edfbb9e6f32a1e799927f05fc3
Closes-Bug: #1746123
This commit is contained in:
Duc Truong 2018-03-14 21:19:41 +00:00
parent 2779e993d8
commit 3f1b2b68b0
3 changed files with 36 additions and 15 deletions

View File

@ -0,0 +1,22 @@
# 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.
from sqlalchemy import MetaData, Numeric, Table
def upgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
table = Table('action', meta, autoload=True)
table.c.start_time.alter(type=Numeric('18,6'))
table.c.end_time.alter(type=Numeric('18,6'))

View File

@ -16,7 +16,7 @@ SQLAlchemy models for Senlin data.
from oslo_db.sqlalchemy import models
from oslo_utils import uuidutils
from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer
from sqlalchemy import Boolean, Column, Numeric, ForeignKey, Integer
from sqlalchemy import String, Text
from sqlalchemy.ext import declarative
from sqlalchemy.orm import backref
@ -228,9 +228,8 @@ class Action(BASE, TimestampMixin, models.ModelBase):
cause = Column(String(255))
owner = Column(String(36))
interval = Column(Integer)
# FIXME: Don't specify fixed precision.
start_time = Column(Float(precision='24,8'))
end_time = Column(Float(precision='24,8'))
start_time = Column(Numeric(18, 6))
end_time = Column(Numeric(18, 6))
timeout = Column(Integer)
status = Column(String(255))
status_reason = Column(Text)

View File

@ -206,7 +206,7 @@ class DBAPIActionTest(base.SenlinTestCase):
self.assertIn(action.name, ('A02', 'A04'))
self.assertEqual('worker2', action.owner)
self.assertEqual(consts.ACTION_RUNNING, action.status)
self.assertEqual(timestamp, action.start_time)
self.assertEqual(timestamp, float(action.start_time))
def test_action_get_all_by_owner(self):
specs = [
@ -283,7 +283,7 @@ class DBAPIActionTest(base.SenlinTestCase):
action1 = db_api.action_get(self.ctx, id_of['A01'])
self.assertEqual('All depended actions completed.',
action1.status_reason)
self.assertEqual(timestamp, action1.end_time)
self.assertEqual(round(timestamp, 6), float(action1.end_time))
def _check_dependency_add_dependent_list(self):
specs = [
@ -376,7 +376,7 @@ class DBAPIActionTest(base.SenlinTestCase):
action = db_api.action_get(self.ctx, id_of['A01'])
self.assertEqual(consts.ACTION_SUCCEEDED, action.status)
self.assertEqual(timestamp, action.end_time)
self.assertEqual(round(timestamp, 6), float(action.end_time))
for aid in [id_of['A02'], id_of['A03'], id_of['A04']]:
res = db_api.dependency_get_dependents(self.ctx, aid)
@ -451,12 +451,12 @@ class DBAPIActionTest(base.SenlinTestCase):
action = db_api.action_get(self.ctx, aid)
self.assertEqual(consts.ACTION_FAILED, action.status)
self.assertEqual('BOOM', action.status_reason)
self.assertEqual(timestamp, action.end_time)
self.assertEqual(round(timestamp, 6), float(action.end_time))
action = db_api.action_get(self.ctx, id_of['A01'])
self.assertEqual(consts.ACTION_FAILED, action.status)
self.assertEqual('BOOM', action.status_reason)
self.assertEqual(timestamp, action.end_time)
self.assertEqual(round(timestamp, 6), float(action.end_time))
for aid in [id_of['A02'], id_of['A03'], id_of['A04']]:
result = db_api.dependency_get_dependents(self.ctx, aid)
@ -473,17 +473,17 @@ class DBAPIActionTest(base.SenlinTestCase):
action = db_api.action_get(self.ctx, aid)
self.assertEqual(consts.ACTION_INIT, action.status)
self.assertNotEqual('BOOM', action.status_reason)
self.assertNotEqual(timestamp, action.end_time)
self.assertIsNone(action.end_time)
action = db_api.action_get(self.ctx, id_of['A01'])
self.assertEqual(consts.ACTION_WAITING, action.status)
self.assertNotEqual('BOOM', action.status_reason)
self.assertNotEqual(timestamp, action.end_time)
self.assertIsNone(action.end_time)
action_d = db_api.action_get(self.ctx, id_of['A02'])
self.assertEqual(consts.ACTION_FAILED, action_d.status)
self.assertEqual('BOOM', action_d.status_reason)
self.assertEqual(timestamp, action_d.end_time)
self.assertEqual(round(timestamp, 6), float(action_d.end_time))
for aid in [id_of['A03'], id_of['A04']]:
result = db_api.dependency_get_dependents(self.ctx, aid)
@ -499,7 +499,7 @@ class DBAPIActionTest(base.SenlinTestCase):
for aid in [id_of['A05'], id_of['A06'], id_of['A07']]:
action = db_api.action_get(self.ctx, aid)
self.assertEqual(consts.ACTION_FAILED, action.status)
self.assertEqual(timestamp, action.end_time)
self.assertEqual(round(timestamp, 6), float(action.end_time))
result = db_api.dependency_get_dependents(self.ctx, id_of['A01'])
self.assertEqual(0, len(result))
@ -512,7 +512,7 @@ class DBAPIActionTest(base.SenlinTestCase):
for aid in [id_of['A05'], id_of['A06'], id_of['A07']]:
action = db_api.action_get(self.ctx, aid)
self.assertEqual(consts.ACTION_CANCELLED, action.status)
self.assertEqual(timestamp, action.end_time)
self.assertEqual(round(timestamp, 6), float(action.end_time))
result = db_api.dependency_get_dependents(self.ctx, id_of['A01'])
self.assertEqual(0, len(result))
@ -539,7 +539,7 @@ class DBAPIActionTest(base.SenlinTestCase):
action = db_api.action_get(self.ctx, id_of['A01'])
self.assertEqual(consts.ACTION_READY, action.status)
self.assertEqual(timestamp, action.end_time)
self.assertEqual(round(timestamp, 6), float(action.end_time))
def test_action_acquire(self):
action = _create_action(self.ctx)