ovn: Always use UTC for Hash ring timestamps

When a node is created in ovn_hash_ring table, it used to use timestamp
depending on the system or mysql timezone. This caused troubles for
machines using western timezones when getting active nodes before the
first liveness check, becaus the time difference between UTC and local
time was always in hours and the limit is 30 seconds.

This patch configures OVN Hash Ring model to always use UTC when
creating a node so the times are always compared to UTC, regardless of
the used timezone. Note that the created_at and updated_at are not user
facing columns.

Change-Id: I9172254ce9b1880833128bdcaacacb93821933dd
Closes-bug: #1894117
Signed-off-by: Jakub Libosvar <libosvar@redhat.com>
This commit is contained in:
Jakub Libosvar 2020-09-08 04:38:42 -04:00
parent 5e942ff21a
commit a84462f698
4 changed files with 20 additions and 4 deletions

View File

@ -54,7 +54,11 @@ class HashRingManager(object):
nodes = db_hash_ring.get_active_nodes(
self.admin_ctx,
constants.HASH_RING_CACHE_TIMEOUT, self._group, from_host=True)
dont_cache = nodes and nodes[0].created_at == nodes[0].updated_at
# created_at and updated_at differ in microseonds so we compare their
# difference is less than a second to be safe on slow machines
dont_cache = nodes and (
nodes[0].updated_at - nodes[0].created_at < datetime.timedelta(
seconds=1))
if not dont_cache:
self._cache_startup_timeout = False

View File

@ -14,6 +14,7 @@
# under the License.
from neutron_lib.db import model_base
from oslo_utils import timeutils
import sqlalchemy as sa
from sqlalchemy.dialects import sqlite
@ -51,9 +52,9 @@ class OVNHashRing(model_base.BASEV2):
node_uuid = sa.Column(sa.String(36), nullable=False, index=True)
group_name = sa.Column(sa.String(256), nullable=False, index=True)
hostname = sa.Column(sa.String(256), nullable=False)
created_at = sa.Column(sa.DateTime(), default=sa.func.now(),
created_at = sa.Column(sa.DateTime(), default=timeutils.utcnow,
nullable=False)
updated_at = sa.Column(sa.DateTime(), default=sa.func.now(),
updated_at = sa.Column(sa.DateTime(), default=timeutils.utcnow,
nullable=False)
__table_args__ = (
sa.PrimaryKeyConstraint(

View File

@ -14,6 +14,7 @@
# under the License.
import datetime
import time
from unittest import mock
from neutron_lib import context
@ -118,6 +119,7 @@ class TestHashRingManager(testlib_api.SqlTestCaseLight):
self.assertTrue(self.hash_ring_manager._cache_startup_timeout)
# Touch the nodes (== update the updated_at column)
time.sleep(1)
db_hash_ring.touch_nodes_from_host(
self.admin_ctx, HASH_RING_TEST_GROUP)

View File

@ -14,6 +14,7 @@
# under the License.
import datetime
import time
from unittest import mock
from neutron_lib import context
@ -44,9 +45,13 @@ class TestHashRing(testlib_api.SqlTestCaseLight):
def _get_node_row(self, node_uuid):
try:
with db_api.CONTEXT_WRITER.using(self.admin_ctx):
return self.admin_ctx.session.query(
node = self.admin_ctx.session.query(
ovn_models.OVNHashRing).filter_by(
node_uuid=node_uuid).one()
# Ignore miliseconds
node.created_at = node.created_at.replace(microsecond=0)
node.updated_at = node.updated_at.replace(microsecond=0)
return node
except exc.NoResultFound:
return
@ -97,6 +102,7 @@ class TestHashRing(testlib_api.SqlTestCaseLight):
self.assertEqual(node_db.created_at, node_db.updated_at)
# Touch the nodes from our host
time.sleep(1)
ovn_hash_ring_db.touch_nodes_from_host(self.admin_ctx,
HASH_RING_TEST_GROUP)
@ -123,6 +129,7 @@ class TestHashRing(testlib_api.SqlTestCaseLight):
self.admin_ctx, interval=60, group_name=HASH_RING_TEST_GROUP)))
# Substract 60 seconds from utcnow() and touch the nodes from our host
time.sleep(1)
fake_utcnow = timeutils.utcnow() - datetime.timedelta(seconds=60)
with mock.patch.object(timeutils, 'utcnow') as mock_utcnow:
mock_utcnow.return_value = fake_utcnow
@ -161,6 +168,7 @@ class TestHashRing(testlib_api.SqlTestCaseLight):
self.assertEqual(node_db.created_at, node_db.updated_at)
# Touch one of the nodes
time.sleep(1)
ovn_hash_ring_db.touch_node(self.admin_ctx, nodes[0])
# Assert it has been updated
@ -217,6 +225,7 @@ class TestHashRing(testlib_api.SqlTestCaseLight):
self.assertEqual(node_db.created_at, node_db.updated_at)
# Touch the nodes from group1
time.sleep(1)
ovn_hash_ring_db.touch_nodes_from_host(self.admin_ctx,
HASH_RING_TEST_GROUP)