Skip to content

Commit

Permalink
feat: Add native retention concept
Browse files Browse the repository at this point in the history
- Add option `system.event-retention-days`.
- Add `Quota.get_event_retention(organization)` API.
- Limit search and event endpoint queries to retention days.
  • Loading branch information
dcramer committed Mar 14, 2018
1 parent 7060449 commit badb368
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 5 deletions.
16 changes: 13 additions & 3 deletions src/sentry/api/endpoints/group_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

import six

from sentry import tagstore
from datetime import timedelta
from django.db.models import Q
from django.utils import timezone
from rest_framework.response import Response

from sentry import quotas, tagstore
from sentry.api.base import DocSection, EnvironmentMixin
from sentry.api.bases import GroupEndpoint
from sentry.api.serializers import serialize
from sentry.api.paginator import DateTimePaginator
from sentry.models import Environment, Event, Group
from sentry.search.utils import parse_query
from sentry.utils.apidocs import scenario, attach_scenarios
from rest_framework.response import Response
from sentry.search.utils import InvalidQuery
from django.db.models import Q


@scenario('ListAvailableSamples')
Expand Down Expand Up @@ -101,4 +104,11 @@ def respond(queryset):

events = events.filter(id__in=event_ids)

# filter out events which are beyond the retention period
retention = quotas.get_event_retention(organization=group.project.organization)
if retention:
events = events.filter(
datetime__gte=timezone.now() - timedelta(days=retention)
)

return respond(events)
11 changes: 11 additions & 0 deletions src/sentry/api/endpoints/project_events.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from __future__ import absolute_import

from datetime import timedelta
from django.utils import timezone

from sentry import quotas
from sentry.api.base import DocSection
from sentry.api.bases.project import ProjectEndpoint
from sentry.api.serializers import serialize
Expand Down Expand Up @@ -45,6 +49,13 @@ def get(self, request, project):
message__icontains=query,
)

# filter out events which are beyond the retention period
retention = quotas.get_event_retention(organization=project.organization)
if retention:
events = events.filter(
datetime__gte=timezone.now() - timedelta(days=retention)
)

return self.paginate(
request=request,
queryset=events,
Expand Down
1 change: 1 addition & 0 deletions src/sentry/options/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
register('system.databases', type=Dict, flags=FLAG_NOSTORE)
# register('system.debug', default=False, flags=FLAG_NOSTORE)
register('system.rate-limit', default=0, flags=FLAG_ALLOW_EMPTY | FLAG_PRIORITIZE_DISK)
register('system.event-retention-days', default=0, flags=FLAG_ALLOW_EMPTY | FLAG_PRIORITIZE_DISK)
register('system.secret-key', flags=FLAG_NOSTORE)
# Absolute URL to the sentry root directory. Should not include a trailing slash.
register('system.url-prefix', ttl=60, grace=3600, flags=FLAG_REQUIRED | FLAG_PRIORITIZE_DISK)
Expand Down
5 changes: 4 additions & 1 deletion src/sentry/quotas/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Quota(Service):
"""
__all__ = (
'get_maximum_quota', 'get_organization_quota', 'get_project_quota', 'is_rate_limited',
'translate_quota', 'validate', 'refund',
'translate_quota', 'validate', 'refund', 'get_event_retention',
)

def __init__(self, **options):
Expand Down Expand Up @@ -137,3 +137,6 @@ def get_maximum_quota(self, organization):
Return the maximum capable rate for an organization.
"""
return (options.get('system.rate-limit'), 60)

def get_event_retention(self, organization):
return options.get('system.event-retention-days') or None
11 changes: 10 additions & 1 deletion src/sentry/search/django/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@

from __future__ import absolute_import

from datetime import timedelta
from django.db import router
from django.db.models import Q
from django.utils import timezone

from sentry import tagstore
from sentry import quotas, tagstore
from sentry.api.paginator import DateTimePaginator, Paginator
from sentry.search.base import EMPTY, SearchBackend
from sentry.search.django.constants import (
Expand Down Expand Up @@ -256,6 +258,13 @@ def _build_queryset(
else:
score_clause = SORT_CLAUSES[sort_by]

# filter out groups which are beyond the retention period
retention = quotas.get_event_retention(organization=project.organization)
if retention:
queryset = queryset.filter(
last_seen__gte=timezone.now() - timedelta(days=retention)
)

queryset = queryset.extra(
select={'sort_value': score_clause},
)
Expand Down
26 changes: 26 additions & 0 deletions tests/sentry/api/endpoints/test_group_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import six

from datetime import timedelta
from django.utils import timezone

from sentry import tagstore
from sentry.models import Environment
from sentry.testutils import APITestCase
Expand Down Expand Up @@ -228,3 +231,26 @@ def test_environment(self):

assert response.status_code == 200, response.content
assert response.data == []

def test_filters_based_on_retention(self):
self.login_as(user=self.user)

project = self.create_project()
group = self.create_group(project=project)
self.create_event(
'a' * 32,
group=group,
datetime=timezone.now() - timedelta(days=2),
)
event_2 = self.create_event('b' * 32, group=group)

with self.options({'system.event-retention-days': 1}):
response = self.client.get('/api/0/issues/{}/events/'.format(group.id))

assert response.status_code == 200, response.content
assert len(response.data) == 1
assert sorted(map(lambda x: x['id'], response.data)) == sorted(
[
six.text_type(event_2.id),
]
)
33 changes: 33 additions & 0 deletions tests/sentry/api/endpoints/test_project_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import six

from datetime import timedelta
from django.utils import timezone
from django.core.urlresolvers import reverse

from sentry.testutils import APITestCase
Expand Down Expand Up @@ -33,3 +35,34 @@ def test_simple(self):
six.text_type(event_2.id),
]
)

def test_filters_based_on_retention(self):
self.login_as(user=self.user)

project = self.create_project()
group = self.create_group(project=project)
self.create_event(
'a' *
32,
group=group,
datetime=timezone.now() - timedelta(days=2),
)
event_2 = self.create_event('b' * 32, group=group)

with self.options({'system.event-retention-days': 1}):
url = reverse(
'sentry-api-0-project-events',
kwargs={
'organization_slug': project.organization.slug,
'project_slug': project.slug,
}
)
response = self.client.get(url, format='json')

assert response.status_code == 200, response.content
assert len(response.data) == 1
assert sorted(map(lambda x: x['id'], response.data)) == sorted(
[
six.text_type(event_2.id),
]
)
11 changes: 11 additions & 0 deletions tests/sentry/api/endpoints/test_project_group_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,17 @@ def test_pending_delete_pending_merge_excluded(self):
assert len(response.data) == 1
assert response.data[0]['id'] == six.text_type(group.id)

def test_filters_based_on_retention(self):
self.login_as(user=self.user)

self.create_group(last_seen=timezone.now() - timedelta(days=2))

with self.options({'system.event-retention-days': 1}):
response = self.client.get(self.path)

assert response.status_code == 200, response.content
assert len(response.data) == 0


class GroupUpdateTest(APITestCase):
@fixture
Expand Down

0 comments on commit badb368

Please sign in to comment.