feat(grouper): add counters to the grouper worker#521
Conversation
Please add a PR description 🙂Respect the reviewers — a description helps others understand the changes and review them faster. Keep it short, clear, and to the point. It also serves as documentation for future reference. The PR was moved to Draft until a description is added. |
e374a3f to
8777e77
Compare
Please add a PR description 🙂Respect the reviewers — a description helps others understand the changes and review them faster. Keep it short, clear, and to the point. It also serves as documentation for future reference. The PR was moved to Draft until a description is added. |
2 similar comments
Please add a PR description 🙂Respect the reviewers — a description helps others understand the changes and review them faster. Keep it short, clear, and to the point. It also serves as documentation for future reference. The PR was moved to Draft until a description is added. |
Please add a PR description 🙂Respect the reviewers — a description helps others understand the changes and review them faster. Keep it short, clear, and to the point. It also serves as documentation for future reference. The PR was moved to Draft until a description is added. |
|
Thanks for adding a description — the PR is now marked as Ready for Review. |
There was a problem hiding this comment.
Pull request overview
This PR moves “events accepted” counter updates into the Grouper worker so project charts are updated only after an event is persisted, and introduces RedisTimeSeries helper methods to support that flow.
Changes:
- Add RedisTimeSeries helper methods (
TS.CREATE,TS.ADD,TS.INCRBY) with “safe” wrappers that create series on-demand. - Record
events-acceptedmetrics (minutely/hourly/daily) at the end ofGrouperWorker.handle. - Add tests asserting the three writes happen, failures are logged without breaking processing, and metrics are recorded once per event.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| workers/grouper/tests/index.test.ts | Adds tests validating metrics writes and failure behavior. |
| workers/grouper/src/redisHelper.ts | Adds RedisTimeSeries command helpers and safe wrappers. |
| workers/grouper/src/index.ts | Records project metrics (3 granularities) after event handling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private buildLabelArguments(labels: Record<string, string>): string[] { | ||
| const labelArgs: string[] = [ 'LABELS' ]; | ||
|
|
||
| for (const [labelKey, labelValue] of Object.entries(labels)) { | ||
| labelArgs.push(labelKey, labelValue); | ||
| } | ||
|
|
||
| return labelArgs; |
There was a problem hiding this comment.
buildLabelArguments always returns ['LABELS'] even when labels is empty. Because tsAdd/tsIncrBy default labels = {}, this can generate RedisTimeSeries commands ending with a bare LABELS token, which Redis will reject as a wrong-arity command. Consider returning an empty array when there are no labels (or make labels required for these public methods).
There was a problem hiding this comment.
we never pass empty labels object
workers/grouper/src/redisHelper.ts
Outdated
|
|
||
| args.push(...this.buildLabelArguments(labels)); | ||
|
|
||
| await this.redisClient.sendCommand(args); |
There was a problem hiding this comment.
tsCreateIfNotExists does a separate EXISTS check and then TS.CREATE. Under concurrency, two workers can both observe the key as missing and then one of the TS.CREATE calls will fail with "key already exists", which currently bubbles up and causes the metric write to be dropped. Consider handling the "already exists" error from TS.CREATE (treat as success) or using an atomic pattern so concurrent creation is safe.
| await this.redisClient.sendCommand(args); | |
| try { | |
| await this.redisClient.sendCommand(args); | |
| } catch (error) { | |
| /** | |
| * Handle race condition where another worker created the key between | |
| * the EXISTS check and TS.CREATE. In that case, RedisTimeSeries will | |
| * return an error like "ERR TSDB: key already exists". We can safely | |
| * treat this as success. | |
| */ | |
| if ( | |
| error instanceof Error && | |
| /key already exists/i.test(error.message) | |
| ) { | |
| return; | |
| } | |
| throw error; | |
| } |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
8dbab2a to
8d54dc9
Compare
* fix(grouper): remove JSON.stringify of full event payload from logs (#525) * feat: Add webhook sender worker for delivering event notifications (#528) * feat: add hawk-worker-webhook service and update package.json * chore: update @hawk.so/types to version 0.5.9 in yarn.lock * refactor: improve webhook delivery mechanism with HTTP/HTTPS support and timeout handling * refactor: replace magic number with constant for HTTP error status in webhook deliverer * feat(webhook): enhance webhook payload structure and add new test command * feat(webhook): unify webhook delivery structure and remove deprecated templates * feat(webhook): expand internal fields filtering in webhook payload * feat(webhook): add private IP checks and DNS validation for webhook delivery * feat(webhook): implement regex-based private IP checks and enhance notification type handling in tests * refactor(webhook): simplify mock implementation for deliverer in tests * feat(webhook): implement SSRF mitigations and enhance private IP checks for webhook delivery * refactor(webhook): streamline webhook deliverer by removing private IP checks and enhancing payload sanitization * refactor(webhook): replace magic number with constant for delivery timeout in webhook deliverer * refactor(webhook): enhance data projection by using specific DB schemes for project, workspace, user, event, and plan DTOs * feat(webhook): add assignee information to webhook payload and update related tests * refactor(webhook): improve redirect handling in webhook deliverer and update test mocks * feat(grouper): add counters to the grouper worker (#521) * chore(grouper): add counters to the grouper worker * chore(): eslint fix * chore(): clean up * chore(grouper): remove redundant rate-limit increment logic * chore(grouper): remove redundant mocks * chore(): eslint fix * chore(): change metric type * Update workers/grouper/src/index.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * imp(): use lua for create if not exists, to avoid race-cond * add logs to sentry worker * chore(): remove redundant import --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Peter Savchenko <specc.dev@gmail.com> --------- Co-authored-by: Kuchizu <70284260+Kuchizu@users.noreply.github.com> Co-authored-by: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Co-authored-by: e11sy <130844513+e11sy@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
Problem
This is the part of the work on sentry counters issue
Sentry events trigger counters (chart) increments and then are silently dismissed in sentry worker
this leads to invalid charts behaviour
Solution
Move charts update to the grouper to guarantee that we increment counters after the moment, when event is stored in the db
Moved charts redis time series logic to grouper
RedisHelperNote
Make sure to check this collector PR also