diff --git a/koji/tasks.py b/koji/tasks.py index ae12eed1..38920c7a 100644 --- a/koji/tasks.py +++ b/koji/tasks.py @@ -889,7 +889,7 @@ class WaitrepoTask(BaseTaskHandler): elif isinstance(newer_than, six.integer_types + (float,)): # here, we look for the first event where the tag changed after this time # or, if the tag has not changed since that time, we use its last change event - base = self.session.getLastEvent(before=newer_than) + base = self.session.getLastEvent(before=newer_than, strict=False) min_event = self.session.tagFirstChangeEvent(tag, after=base) or "last" else: raise koji.GenericError("Invalid value for newer_than: %s" % newer_than) diff --git a/kojihub/kojihub.py b/kojihub/kojihub.py index 3f17e5eb..6f730649 100644 --- a/kojihub/kojihub.py +++ b/kojihub/kojihub.py @@ -11084,7 +11084,7 @@ class RootExports(object): clauses=['id = %(id)s'], values={'id': event_id}) return query.executeOne(strict=strict) - def getLastEvent(self, before=None): + def getLastEvent(self, before=None, strict=True): """ Get the id and timestamp of the last event recorded in the system. Events are usually created as the result of a configuration change @@ -11101,6 +11101,7 @@ class RootExports(object): When trying to find information about a specific event, the getEvent() method should be used. """ + strict = convert_value(strict, cast=bool) fields = [ ('id', 'id'), ("date_part('epoch', time)", 'ts') @@ -11118,7 +11119,7 @@ class RootExports(object): opts = {'order': '-id', 'limit': 1} query = QueryProcessor(tables=['events'], columns=columns, aliases=aliases, clauses=clauses, values=values, opts=opts) - return query.executeOne(strict=True) + return query.executeOne(strict=strict) evalPolicy = staticmethod(eval_policy) diff --git a/kojihub/repos.py b/kojihub/repos.py index e313d80c..5fc08ed1 100644 --- a/kojihub/repos.py +++ b/kojihub/repos.py @@ -498,7 +498,13 @@ def do_auto_requests(): lag = lags.get(tag_id, default_lag) base_ts = time.time() - lag base_ts = (base_ts // window) * window - base = context.handlers.get('getLastEvent')(before=base_ts)['id'] + base_ev = context.handlers.get('getLastEvent')(before=base_ts, strict=False) + if base_ev: + base = base_ev['id'] + else: + # this will only happen with a brand new instance + base = kojihub.tag_first_change_event(tag_id) + logger.debug(f'No event older than {base_ts}, using first tag event {base}') check = request_repo(tag_id, min_event=min(base, last), priority=5) # lower priority so they don't block on-demand if check['duplicate']: @@ -814,7 +820,13 @@ def default_min_event(taginfo): # We round base_ts to nearest window so that duplicate requests will get same event if they # are close in time. base_ts = (base_ts // window) * window - base = context.handlers.get('getLastEvent')(before=base_ts)['id'] + base_ev = context.handlers.get('getLastEvent')(before=base_ts, strict=False) + if base_ev: + base = base_ev['id'] + else: + # this will only happen with a brand new instance + base = kojihub.tag_first_change_event(taginfo['id']) + logger.debug(f'No event older than {base_ts}, using first tag event {base}') # If the tag has changed recently, we allow a bit of lag. # Otherwise, we use the most recent event for the tag. return min(base, last) diff --git a/tests/test_hub/test_repo_requests.py b/tests/test_hub/test_repo_requests.py index 180ca4b1..ca919d71 100644 --- a/tests/test_hub/test_repo_requests.py +++ b/tests/test_hub/test_repo_requests.py @@ -66,6 +66,7 @@ class BaseTest(unittest.TestCase): self.get_id = mock.patch('kojihub.kojihub.get_id').start() self.make_task = mock.patch('kojihub.kojihub.make_task').start() self.tag_last_change_event = mock.patch('kojihub.kojihub.tag_last_change_event').start() + self.tag_first_change_event = mock.patch('kojihub.kojihub.tag_first_change_event').start() self.query_execute = mock.MagicMock() self.query_executeOne = mock.MagicMock() self.query_singleValue = mock.MagicMock() @@ -603,8 +604,6 @@ class TestUpdateEndEvents(BaseTest): def setUp(self): super(TestUpdateEndEvents, self).setUp() self.BulkUpdateProcessor = mock.patch('kojihub.repos.BulkUpdateProcessor').start() - self.tag_first_change_event = mock.patch('kojihub.kojihub.tag_first_change_event').start() - self.tag_last_change_event = mock.patch('kojihub.kojihub.tag_last_change_event').start() def test_no_update(self): repo = {'id': 1, 'tag_id': 99, 'create_event': 1000} @@ -767,7 +766,7 @@ class TestAutoRequests(BaseTest): self.request_repo.called_once_with(99, min_event=1000, priority=5) # with zero lag, getLastEvent should be called with current time - self.getLastEvent.assert_called_once_with(before=now) + self.getLastEvent.assert_called_once_with(before=now, strict=False) def test_auto_lag_window(self): self.context.opts['RepoLagWindow'] = 600 @@ -792,7 +791,7 @@ class TestAutoRequests(BaseTest): if before > now or before < now - 600: raise Exception('Invalid lag calculation') - def test_no_last_event(self): + def test_no_last_tag_event(self): # corner case that should not happen autokeys = [ {'tag_id': 99, 'key': 'repo.auto', 'value': 'true'}, @@ -805,6 +804,26 @@ class TestAutoRequests(BaseTest): self.request_repo.assert_not_called() self.tag_last_change_event.assert_called_once() + def test_no_last_event(self): + # corner case that can happen with very new instances + autokeys = [ + {'tag_id': 99, 'key': 'repo.auto', 'value': 'true'}, + ] + self.getLastEvent.return_value = None + self.query_execute.return_value = autokeys + self.tag_last_change_event.return_value = 1000 + self.tag_first_change_event.return_value = 990 + self.request_repo.return_value = {'repo': None, 'request': 'REQ', 'duplicate': False} + + repos.do_auto_requests() + + self.request_repo.called_once_with(99, min_event=990, priority=5) + self.tag_last_change_event.assert_called_once() + self.tag_first_change_event.assert_called_once() + + repos.do_auto_requests() + + class TestGetRepo(BaseTest): @@ -1303,6 +1322,22 @@ class TestDefaultMinEvent(BaseTest): base_ts = self.getLastEvent.call_args.kwargs['before'] self.assertEqual(base_ts, now - 3600) + def test_no_last_event(self): + # corner case that can happen with very new instances + now = 1717171717 + self.time.return_value = now + taginfo = {'id': 55, 'name': 'MYTAG', 'extra': {}} + self.tag_last_change_event.return_value = 10000 + self.getLastEvent.return_value = None + self.tag_first_change_event.return_value = 9990 + + ev = repos.default_min_event(taginfo) + + # in this corner case we should use the first event for the tag + self.assertEqual(ev, 9990) + self.getLastEvent.assert_called_once() + self.tag_first_change_event.assert_called_once_with(55) + class TestCheckRequest(BaseTest):