{"action":"created","issue":{"url":"https://api.github.com/repos/getsentry/sentry/issues/6465","repository_url":"https://api.github.com/repos/getsentry/sentry","labels_url":"https://api.github.com/repos/getsentry/sentry/issues/6465/labels{/name}","comments_url":"https://api.github.com/repos/getsentry/sentry/issues/6465/comments","events_url":"https://api.github.com/repos/getsentry/sentry/issues/6465/events","html_url":"https://github.com/getsentry/sentry/issues/6465","id":269247735,"number":6465,"title":"Storing Mutation Data with Analytics Events","user":{"login":"ehfeng","id":279398,"avatar_url":"https://avatars0.githubusercontent.com/u/279398?v=4","gravatar_id":"","url":"https://api.github.com/users/ehfeng","html_url":"https://github.com/ehfeng","followers_url":"https://api.github.com/users/ehfeng/followers","following_url":"https://api.github.com/users/ehfeng/following{/other_user}","gists_url":"https://api.github.com/users/ehfeng/gists{/gist_id}","starred_url":"https://api.github.com/users/ehfeng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ehfeng/subscriptions","organizations_url":"https://api.github.com/users/ehfeng/orgs","repos_url":"https://api.github.com/users/ehfeng/repos","events_url":"https://api.github.com/users/ehfeng/events{/privacy}","received_events_url":"https://api.github.com/users/ehfeng/received_events","type":"User","site_admin":false},"labels":[{"id":325759515,"url":"https://api.github.com/repos/getsentry/sentry/labels/Type:%20Discussion","name":"Type: Discussion","color":"fad8c7","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":1,"created_at":"2017-10-27T21:56:19Z","updated_at":"2017-10-27T23:03:35Z","closed_at":null,"author_association":"MEMBER","body":"Stateless event data is straightforward (Jenny clicked the button yesterday), but often, we want to know *what state* changed without significantly modifying Sentry's codebase.[^1]\r\n\r\nSo far, we have two approaches: \"events\" and \"mutations.\"\r\n\r\nWith **events**, after the event has occurred, we attach new state to the event. The analytics code is much simpler and less prone to breakage.\r\n\r\n```python\r\nanalytics.record('plan.changed', customer.plan)\r\n```\r\n\r\nWith **mutation**, we must capture the existing plan *prior* to change. This is less idea as we introduce dozens of analytics events to the code base—needing to following all the branching code paths to `e.record` makes yet another state to track within a request.\r\n\r\n```python\r\ne = analytics.build('plan.changed', previous=customer.plan, current=new_plan)\r\n\r\n# ...save new plan...\r\n\r\ne.record()\r\n```\r\n\r\nFor ease of discussion, let's consider someone upgrades their plan. We might ask ourselves, \"What was expansion revenue last month?\" If we record previous and current state with each event, it's easy to select `plan.changed` events and find the delta in the plan prices. However, if we only stored the current plan with events, we'd need to select the last `plan.changed` event since the first of the month to compare it against the most recent data.\r\n\r\nHowever, financial reporting rarely needs *continuous* data (usually reporting is monthly), so even if you stored previous/current state with each event, you'd need to get both the first and last \"plan.changed\" events within a month. The only difference is the \"events\" route, you select the **last** event from the previous month instead of the **first** event of the current.\r\n\r\nAre there other interesting cases where mutation might offer significant advantages to justify the additional complexity? @mattrobenolt @alex-hofsteede @dcramer @sksembhi \r\n\r\n[^1]: Doing full event sourcing with Kafka + rollups might be the academically correct answer, but I'm hoping we can do something simpler and \"good enough\". Because of that, event data doesn't need to be *complete* so much as it needs to be *useable*."},"comment":{"url":"https://api.github.com/repos/getsentry/sentry/issues/comments/340117211","html_url":"https://github.com/getsentry/sentry/issues/6465#issuecomment-340117211","issue_url":"https://api.github.com/repos/getsentry/sentry/issues/6465","id":340117211,"user":{"login":"ehfeng","id":279398,"avatar_url":"https://avatars0.githubusercontent.com/u/279398?v=4","gravatar_id":"","url":"https://api.github.com/users/ehfeng","html_url":"https://github.com/ehfeng","followers_url":"https://api.github.com/users/ehfeng/followers","following_url":"https://api.github.com/users/ehfeng/following{/other_user}","gists_url":"https://api.github.com/users/ehfeng/gists{/gist_id}","starred_url":"https://api.github.com/users/ehfeng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ehfeng/subscriptions","organizations_url":"https://api.github.com/users/ehfeng/orgs","repos_url":"https://api.github.com/users/ehfeng/repos","events_url":"https://api.github.com/users/ehfeng/events{/privacy}","received_events_url":"https://api.github.com/users/ehfeng/received_events","type":"User","site_admin":false},"created_at":"2017-10-27T23:03:35Z","updated_at":"2017-10-27T23:03:35Z","author_association":"MEMBER","body":"> changing a plan is a poor fit for a stream-of-events analytics model\r\n\r\nYes, for understanding a user's choice in a range of time. The potential use case for this would be:\r\n\r\n1. users receive over-quota notifications\r\n2. some users setup rate limits\r\n\r\nI *think* stream-of-events better answers the question, \"How many users setup rate limits because of the notification?\". Right now, organization rate limits are basically mutable attributes on organization—we don't have a log of who changed it to what value when. Adding these kinds of events lets us better understand what's working and what isn't.\r\n\r\n> store keyframe events periodically in the event stream that show the current point-in-time state per user\r\n\r\nAgreed. For that, we've just started snapshotting certain tables.\r\n\r\n> In general I'd recommend just adding as many properties as possible to your analytics events.\r\n\r\nI also agree. What I'm trying to understand if it's worth the added complexity the store before/after state mutation, given the extra complexity. If there are potential analyses that need it, then I'll add. But financial reporting is better done through snapshotting, and I can't think of other valuable analyses that would need it, so I don't think the extra complexity is worth it."}}
{"actor":{"display_login":"ehfeng"}}