mirror of
https://github.com/jlengrand/bugsink.git
synced 2026-03-10 08:01:17 +00:00
vbc-unmute: reduce calls to the expensive check
as done in the previous commit for project quota
This commit is contained in:
@@ -290,6 +290,8 @@ class BaseIngestAPIView(View):
|
||||
# nothing to check (otherwise) or update on project in this case, also abort further event-processing
|
||||
return False
|
||||
|
||||
project.digested_event_count += 1
|
||||
|
||||
if project.digested_event_count >= project.next_quota_check:
|
||||
# check_for_thresholds is relatively expensive (SQL group by); we do it as little as possible
|
||||
|
||||
@@ -314,33 +316,41 @@ class BaseIngestAPIView(View):
|
||||
# next_quota_check (the if-statement) and the setting (the statement below) we're good.
|
||||
project.next_quota_check = project.digested_event_count + check_again_after
|
||||
|
||||
project.digested_event_count += 1
|
||||
project.save()
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def count_issue_periods_and_act_on_it(cls, issue, event, timestamp):
|
||||
# See the project-version for various off-by-one notes (not reproduced here).
|
||||
#
|
||||
# We just have "unmute" as a purpose here, not "quota". I thought I'd have per-issue quota earlier (which would
|
||||
# ensure some kind of fairness within a project) but:
|
||||
#
|
||||
# * that doesn't quite work, because to determine the issue, you'd have to incur almost all of the digest cost.
|
||||
# * quota are expected to be set "high enough" anyway, i.e. only as a last line of defense against run-away
|
||||
# clients
|
||||
# * "even if" you'd get this to work there'd be scenarios where it's useless, e.g. misbehaving groupers.
|
||||
thresholds = IssueStateManager.get_unmute_thresholds(issue)
|
||||
|
||||
states = check_for_thresholds(Event.objects.filter(issue=issue), timestamp, thresholds)
|
||||
if thresholds and issue.digested_event_count >= issue.next_unmute_check:
|
||||
states = check_for_thresholds(Event.objects.filter(issue=issue), timestamp, thresholds)
|
||||
|
||||
for (state, until, _, vbc_dict) in states:
|
||||
if not state:
|
||||
continue
|
||||
check_again_after = max(1, min([check_after for (_, _, check_after, _) in states], default=1))
|
||||
|
||||
IssueStateManager.unmute(issue, triggering_event=event, unmute_metadata={"mute_until": vbc_dict})
|
||||
issue.next_unmute_check = issue.digested_event_count + check_again_after
|
||||
|
||||
# In the (in the current UI impossible, and generally unlikely) case that multiple unmute conditions are met
|
||||
# simultaneously, we arbitrarily break after the first. (this makes it so that a single TurningPoint is
|
||||
# created and that the detail that there was also another reason to unmute doesn't show us, but that's
|
||||
# perfectly fine); it also matches what we do elsewhere (i.e. 'if is_muted` in `IssueStateManager.unmute`)
|
||||
break
|
||||
for (state, until, _, vbc_dict) in states:
|
||||
if not state:
|
||||
continue
|
||||
|
||||
IssueStateManager.unmute(issue, triggering_event=event, unmute_metadata={"mute_until": vbc_dict})
|
||||
|
||||
# In the (in the current UI impossible, and generally unlikely) case that multiple unmute conditions are
|
||||
# met simultaneously, we arbitrarily break after the first. (this makes it so that a single TurningPoint
|
||||
# is created and that the detail that there was also another reason to unmute doesn't show us, but
|
||||
# that's perfectly fine); it also matches what we do elsewhere (i.e. `IssueStateManager.unmute` where we
|
||||
# have `if is_muted`)
|
||||
break
|
||||
|
||||
|
||||
class IngestEventAPIView(BaseIngestAPIView):
|
||||
|
||||
18
issues/migrations/0006_issue_next_unmute_check.py
Normal file
18
issues/migrations/0006_issue_next_unmute_check.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-17 13:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('issues', '0005_rename_ingest_order_issue_digest_order_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='issue',
|
||||
name='next_unmute_check',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
]
|
||||
@@ -58,6 +58,7 @@ class Issue(models.Model):
|
||||
is_muted = models.BooleanField(default=False)
|
||||
unmute_on_volume_based_conditions = models.TextField(blank=False, null=False, default="[]") # json string
|
||||
unmute_after = models.DateTimeField(blank=True, null=True)
|
||||
next_unmute_check = models.PositiveIntegerField(null=False, default=0)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.digest_order is None:
|
||||
@@ -199,6 +200,10 @@ class IssueStateManager(object):
|
||||
|
||||
issue.is_muted = True
|
||||
issue.unmute_on_volume_based_conditions = unmute_on_volume_based_conditions
|
||||
# 0 is "incorrect" but works just fine; it simply means that the first (real, but expensive) check is done
|
||||
# on-digest. However, to calculate the correct value we'd need to do that work right now, so postponing is
|
||||
# actually better. Setting to 0 is still needed to ensure the check is done when there was already a value.
|
||||
issue.next_unmute_check = 0
|
||||
|
||||
if unmute_after is not None:
|
||||
issue.unmute_after = unmute_after
|
||||
@@ -333,6 +338,7 @@ class IssueQuerysetStateManager(object):
|
||||
issue_qs.update(
|
||||
is_muted=True,
|
||||
unmute_on_volume_based_conditions=unmute_on_volume_based_conditions,
|
||||
next_unmute_check=0,
|
||||
)
|
||||
|
||||
if unmute_after is not None:
|
||||
|
||||
Reference in New Issue
Block a user