Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(scheduled flag changes): implement scheduling of changes #19395

Merged
merged 78 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
9d277aa
add plain UI
jurajmajerik Dec 5, 2023
7d49c4c
Update UI snapshots for `chromium` (1)
github-actions[bot] Dec 5, 2023
7d51dbc
resolve conflict
jurajmajerik Dec 6, 2023
2563319
Merge branch 'master' into feat/schedule-feature-flag
jurajmajerik Dec 6, 2023
8496142
Merge branch 'master' into feat/schedule-feature-flag
jurajmajerik Dec 6, 2023
f72db3d
Merge branch 'master' into feat/schedule-feature-flag
jurajmajerik Dec 7, 2023
1d5c0ac
add schedule changes API
jurajmajerik Dec 8, 2023
2be18e8
Merge branch 'master' of https://github.com/PostHog/posthog into feat…
jurajmajerik Dec 8, 2023
473b9bd
add migrations
jurajmajerik Dec 8, 2023
92b4bad
adjust model
jurajmajerik Dec 12, 2023
59d9e00
Merge branch 'master' of https://github.com/PostHog/posthog into feat…
jurajmajerik Dec 12, 2023
232175b
adjust
jurajmajerik Dec 12, 2023
845bce5
connect frontend to the API
jurajmajerik Dec 13, 2023
10132ef
resolve conflict
jurajmajerik Dec 14, 2023
8bff377
clarify
jurajmajerik Dec 14, 2023
d00e33d
Update UI snapshots for `chromium` (2)
github-actions[bot] Dec 14, 2023
e317449
Update UI snapshots for `chromium` (2)
github-actions[bot] Dec 14, 2023
20b2a8e
Update UI snapshots for `chromium` (2)
github-actions[bot] Dec 14, 2023
2c7be9e
Update query snapshots
github-actions[bot] Dec 14, 2023
4cd2bcf
Update UI snapshots for `chromium` (2)
github-actions[bot] Dec 14, 2023
20fe30a
Update query snapshots
github-actions[bot] Dec 14, 2023
60fd6a2
rename setId -> setFeatureFlagId
jurajmajerik Dec 14, 2023
7b0b30e
Merge branch 'feat/schedule-feature-flag' of https://github.com/PostH…
jurajmajerik Dec 14, 2023
99099e2
Merge branch 'master' into feat/schedule-feature-flag
jurajmajerik Dec 15, 2023
a22e702
Merge branch 'master' into feat/schedule-feature-flag
jurajmajerik Dec 15, 2023
a5289e8
update latest migrations
jurajmajerik Dec 16, 2023
74f98dc
Merge branch 'feat/schedule-feature-flag' of https://github.com/PostH…
jurajmajerik Dec 16, 2023
7978672
Merge branch 'master' of https://github.com/PostHog/posthog into feat…
jurajmajerik Dec 18, 2023
5c71062
Merge branch 'master' of https://github.com/PostHog/posthog into feat…
jurajmajerik Dec 18, 2023
60ea996
implement scheduling of changes
jurajmajerik Dec 18, 2023
2cc0f5d
don't re-run failed changes
jurajmajerik Dec 18, 2023
3286b74
Update posthog/celery.py
jurajmajerik Dec 19, 2023
ac1d83b
process in chunks
jurajmajerik Dec 19, 2023
5fde0f0
add return type
jurajmajerik Dec 20, 2023
f3c283a
order queried changes by scheduled_at
jurajmajerik Dec 20, 2023
5fe726e
add test for multiple scheduled changes
jurajmajerik Dec 20, 2023
5cc9720
Merge branch 'master' into feat/schedule-feature-flag
jurajmajerik Dec 20, 2023
0d78d7a
adjust scheduled_change model
jurajmajerik Dec 21, 2023
bc842e4
limit query to 10,000 records
jurajmajerik Dec 21, 2023
d6b8a59
add query snapshot
jurajmajerik Dec 21, 2023
96131cd
fix conflict
jurajmajerik Dec 21, 2023
7ff2c44
use serializer to update the flag
jurajmajerik Dec 21, 2023
30e47a6
rename payload fields
jurajmajerik Dec 21, 2023
7e418ba
fix conflict
jurajmajerik Dec 21, 2023
acd5fc4
fix migrations
jurajmajerik Dec 21, 2023
f096549
add migrations manifest
jurajmajerik Dec 21, 2023
3059b90
Update query snapshots
github-actions[bot] Dec 21, 2023
049d4a2
Merge branch 'master' into feat/schedule-feature-flag
jurajmajerik Dec 21, 2023
2249402
Update query snapshots
github-actions[bot] Dec 21, 2023
5af6b0a
Update query snapshots
github-actions[bot] Dec 21, 2023
803418e
Update query snapshots
github-actions[bot] Dec 21, 2023
b4f6d2d
Update query snapshots
github-actions[bot] Dec 21, 2023
3e260ba
Update query snapshots
github-actions[bot] Dec 21, 2023
1a0f51a
Update query snapshots
github-actions[bot] Dec 21, 2023
6827222
Update query snapshots
github-actions[bot] Dec 21, 2023
4da809f
Update query snapshots
github-actions[bot] Dec 21, 2023
04a9bb5
fix migrations
jurajmajerik Dec 21, 2023
710cdb6
Merge branch 'feat/schedule-feature-flag' of https://github.com/PostH…
jurajmajerik Dec 21, 2023
edac981
Update query snapshots
github-actions[bot] Dec 21, 2023
c7f1a23
Update query snapshots
github-actions[bot] Dec 21, 2023
41da321
Update query snapshots
github-actions[bot] Dec 21, 2023
9cba2f1
final adjustments
jurajmajerik Dec 21, 2023
1cde7db
fix conflict
jurajmajerik Dec 21, 2023
c77f238
Merge branch 'master' of https://github.com/PostHog/posthog into feat…
jurajmajerik Dec 21, 2023
3c524ef
add index concurrently
jurajmajerik Dec 21, 2023
1ba4020
Merge branch 'master' into feat/schedule-feature-flag
jurajmajerik Dec 21, 2023
77dc563
import AddIndexConcurrently
jurajmajerik Dec 21, 2023
7353f82
Merge branch 'feat/schedule-feature-flag' of https://github.com/PostH…
jurajmajerik Dec 21, 2023
cf5862a
tweak migration
jurajmajerik Dec 21, 2023
674cc7d
Merge branch 'master' into feat/schedule-feature-flag
jurajmajerik Dec 21, 2023
4056337
tweak migration
jurajmajerik Dec 21, 2023
4f19320
Merge branch 'feat/schedule-feature-flag' of https://github.com/PostH…
jurajmajerik Dec 21, 2023
846dfcf
tweak migration
jurajmajerik Dec 21, 2023
dc05e1b
Merge branch 'master' into feat/schedule-feature-flag
jurajmajerik Dec 21, 2023
62340ed
fix API test
jurajmajerik Dec 21, 2023
679052d
Merge branch 'feat/schedule-feature-flag' of https://github.com/PostH…
jurajmajerik Dec 21, 2023
c1466fe
Merge branch 'feat/schedule-feature-flag' of https://github.com/PostH…
jurajmajerik Dec 21, 2023
ca435c2
Merge branch 'feat/schedule-feature-flag' of https://github.com/PostH…
jurajmajerik Dec 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion latest_migrations.manifest
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ contenttypes: 0002_remove_content_type_name
ee: 0015_add_verified_properties
otp_static: 0002_throttling
otp_totp: 0002_auto_20190420_0723
posthog: 0378_alter_user_theme_mode
posthog: 0379_alter_scheduledchange
sessions: 0001_initial
social_django: 0010_uid_db_index
two_factor: 0007_auto_20201201_1019
14 changes: 14 additions & 0 deletions posthog/celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,13 @@ def setup_periodic_tasks(sender: Celery, **kwargs):
name="recalculate cohorts",
)

add_periodic_task_with_expiry(
sender,
120,
process_scheduled_changes.s(),
name="process scheduled changes",
)

if clear_clickhouse_crontab := get_crontab(settings.CLEAR_CLICKHOUSE_REMOVED_DATA_SCHEDULE_CRON):
sender.add_periodic_task(
clear_clickhouse_crontab,
Expand Down Expand Up @@ -871,6 +878,13 @@ def calculate_cohort():
calculate_cohorts()


@app.task(ignore_result=True)
def process_scheduled_changes():
from posthog.tasks.process_scheduled_changes import process_scheduled_changes

process_scheduled_changes()


@app.task(ignore_result=True)
def sync_insight_cache_states_task():
from posthog.caching.insight_caching_state import sync_insight_cache_states
Expand Down
27 changes: 27 additions & 0 deletions posthog/migrations/0379_alter_scheduledchange.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 3.2.19 on 2023-12-21 11:41

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("posthog", "0378_alter_user_theme_mode"),
]

operations = [
migrations.AlterField(
model_name="scheduledchange",
name="executed_at",
field=models.DateTimeField(blank=True, db_index=True, null=True),
),
migrations.AlterField(
model_name="scheduledchange",
name="record_id",
field=models.CharField(max_length=200),
),
migrations.AlterField(
model_name="scheduledchange",
name="scheduled_at",
field=models.DateTimeField(db_index=True),
),
]
25 changes: 25 additions & 0 deletions posthog/models/feature_flag/feature_flag.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,31 @@ def get_cohort_ids(

return list(cohort_ids)

def scheduled_changes_dispatcher(self, payload):
from posthog.api.feature_flag import FeatureFlagSerializer

if "operation" not in payload or "value" not in payload:
raise Exception("Invalid payload")

context = {
"request": {"user": self.created_by},
"team_id": self.team_id,
}
serializer_data = {}

if payload["operation"] == "add_release_condition":
jurajmajerik marked this conversation as resolved.
Show resolved Hide resolved
existing_groups = self.filters.get("groups", [])
jurajmajerik marked this conversation as resolved.
Show resolved Hide resolved
new_groups = payload["value"].get("groups", [])
serializer_data["filters"] = {"groups": existing_groups + new_groups}
elif payload["operation"] == "update_status":
serializer_data["active"] = payload["value"]
else:
raise Exception(f"Unrecognized operation: {payload['operation']}")

serializer = FeatureFlagSerializer(self, data=serializer_data, context=context, partial=True)
if serializer.is_valid(raise_exception=True):
serializer.save()

@property
def uses_cohorts(self) -> bool:
for condition in self.conditions:
Expand Down
7 changes: 3 additions & 4 deletions posthog/models/scheduled_change.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
from django.db import models
from django.utils import timezone


class ScheduledChange(models.Model):
class AllowedModels(models.TextChoices):
FEATURE_FLAG = "FeatureFlag", "feature flag"

id = models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")
record_id = models.IntegerField()
record_id: models.CharField = models.CharField(max_length=200)
model_name: models.CharField = models.CharField(max_length=100, choices=AllowedModels.choices)
payload: models.JSONField = models.JSONField(default=dict)
scheduled_at: models.DateTimeField = models.DateTimeField(default=timezone.now)
executed_at: models.DateTimeField = models.DateTimeField(null=True, blank=True)
scheduled_at: models.DateTimeField = models.DateTimeField(db_index=True)
jurajmajerik marked this conversation as resolved.
Show resolved Hide resolved
executed_at: models.DateTimeField = models.DateTimeField(null=True, blank=True, db_index=True)
failure_reason = models.CharField(max_length=400, null=True, blank=True)

team: models.ForeignKey = models.ForeignKey("Team", on_delete=models.CASCADE)
Expand Down
2 changes: 2 additions & 0 deletions posthog/tasks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
demo_create_data,
email,
exporter,
process_scheduled_changes,
split_person,
sync_all_organization_available_features,
usage_report,
Expand All @@ -20,6 +21,7 @@
"demo_create_data",
"email",
"exporter",
"process_scheduled_changes",
"split_person",
"sync_all_organization_available_features",
"user_identify",
Expand Down
39 changes: 39 additions & 0 deletions posthog/tasks/process_scheduled_changes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from posthog.models import ScheduledChange
from django.utils import timezone
from posthog.models import FeatureFlag
from django.db import transaction, OperationalError

models = {"FeatureFlag": FeatureFlag}


def process_scheduled_changes() -> None:
try:
with transaction.atomic():
scheduled_changes = (
ScheduledChange.objects.select_for_update(nowait=True)
.filter(
executed_at__isnull=True,
scheduled_at__lt=timezone.now(),
)
.order_by("scheduled_at")[:10000]
)

for scheduled_change in scheduled_changes:
try:
# Execute the change on the model instance
model = models[scheduled_change.model_name]
instance = model.objects.get(id=scheduled_change.record_id)
instance.scheduled_changes_dispatcher(scheduled_change.payload)

# Mark scheduled change completed
scheduled_change.executed_at = timezone.now()
scheduled_change.save()

except Exception as e:
# Store the failure reason
scheduled_change.failure_reason = str(e)
scheduled_change.executed_at = timezone.now()
scheduled_change.save()
except OperationalError:
jurajmajerik marked this conversation as resolved.
Show resolved Hide resolved
# Failed to obtain the lock
jurajmajerik marked this conversation as resolved.
Show resolved Hide resolved
pass
Loading
Loading