Skip to content

Commit

Permalink
feat(crons): Support empty check_in_id in consumer
Browse files Browse the repository at this point in the history
This is to support getsentry/relay#2153

Provdes a means to creating check-ins without giving a check_in_id in
the consumer payload.

Documentation for this is here getsentry/develop#950

Fixes: #49664
  • Loading branch information
evanpurkhiser committed Jun 2, 2023
1 parent 46e7102 commit e3cd504
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 6 deletions.
32 changes: 27 additions & 5 deletions src/sentry/monitors/consumers/monitor_consumer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime
import logging
import uuid
from typing import Dict, Mapping, Optional

import msgpack
Expand Down Expand Up @@ -165,10 +166,25 @@ def _process_message(wrapper: Dict) -> None:
else None
)

# Invalid UUIDs will raise ValueError
check_in_id = uuid.UUID(params["check_in_id"])

# When the UUID is empty we will default to looking for the most
# recent check-in which is not in a terminal state.
use_latest_checkin = check_in_id.int == 0

try:
check_in = MonitorCheckIn.objects.select_for_update().get(
guid=params["check_in_id"],
)
if use_latest_checkin:
check_in = (
MonitorCheckIn.objects.select_for_update()
.exclude(status__in=CheckInStatus.FINISHED_VALUES)
.order_by("-date_added")[:1]
.get()
)
else:
check_in = MonitorCheckIn.objects.select_for_update().get(
guid=check_in_id,
)

if (
check_in.project_id != project_id
Expand All @@ -181,7 +197,7 @@ def _process_message(wrapper: Dict) -> None:
)
logger.debug(
"check-in guid %s already associated with %s not payload %s",
params["check_in_id"],
check_in_id,
check_in.monitor_id,
monitor.id,
)
Expand Down Expand Up @@ -233,11 +249,17 @@ def _process_message(wrapper: Dict) -> None:
monitor_environment.last_checkin
)

# If the UUID is unset (zero value) generate a new UUID
if check_in_id.int == 0:
guid = uuid.uuid4()
else:
guid = check_in_id

check_in = MonitorCheckIn.objects.create(
project_id=project_id,
monitor=monitor,
monitor_environment=monitor_environment,
guid=params["check_in_id"],
guid=guid,
duration=duration,
status=status,
date_added=date_added,
Expand Down
44 changes: 43 additions & 1 deletion tests/sentry/monitors/test_monitor_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@

class MonitorConsumerTest(TestCase):
def get_message(
self, monitor_slug: str, guid: Optional[str] = None, **overrides: Any
self,
monitor_slug: str,
guid: Optional[str] = None,
**overrides: Any,
) -> Dict[str, Any]:
now = datetime.now()
self.guid = uuid.uuid4().hex if not guid else guid

payload = {
"monitor_slug": monitor_slug,
"status": "ok",
Expand Down Expand Up @@ -273,6 +277,44 @@ def test_monitor_update(self):
== monitor_environment.monitor.get_next_scheduled_checkin(checkin.date_added)
)

@pytest.mark.django_db
def test_check_in_empty_id(self):
monitor = self._create_monitor(slug="my-monitor")
message = self.get_message(
"my-monitor",
guid=str(uuid.UUID(int=0)),
)
_process_message(message)

checkin = MonitorCheckIn.objects.get(monitor=monitor)
assert checkin.status == CheckInStatus.OK
assert checkin.guid.int != 0

@pytest.mark.django_db
def test_check_in_empty_id_update(self):
monitor = self._create_monitor(slug="my-monitor")
message_open = self.get_message(
"my-monitor",
status="in_progress",
guid=str(uuid.UUID(int=0)),
)
_process_message(message_open)

open_checkin = MonitorCheckIn.objects.get(monitor=monitor)
assert open_checkin.status == CheckInStatus.IN_PROGRESS
assert open_checkin.guid != uuid.UUID(int=0)

message_close = self.get_message(
"my-monitor",
status="ok",
guid=str(uuid.UUID(int=0)),
)
_process_message(message_close)

close_checkin = MonitorCheckIn.objects.get(guid=open_checkin.guid)
assert close_checkin.status == CheckInStatus.OK
assert close_checkin.guid != uuid.UUID(int=0)

def test_rate_limit(self):
monitor = self._create_monitor(slug="my-monitor")

Expand Down

0 comments on commit e3cd504

Please sign in to comment.