-
Notifications
You must be signed in to change notification settings - Fork 125
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
Throttle analytics to 300 daily events per dev machine #4424
Conversation
We detected some changes at either packages/*/src or packages/cli-kit/assets/cli-ruby/** and there are no updates in the .changeset. |
Coverage report
Show files with reduced coverage 🔻
Test suite run success1874 tests passing in 850 suites. Report generated by 🧪jest coverage report action from 5614c49 |
580fa24
to
95fa096
Compare
64923ea
to
6be53e3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One suggestion but rest looks great 👌
export async function runWithRateLimit( | ||
options: RunWithRateLimitOptions, | ||
config = cliKitStore(), | ||
): Promise<true | undefined> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that this is only used in a pattern where the provided task
is a stub, maybe we should narrow this down, drop the task
piece and just return true/false. Can always reintroduce something later should the need come.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that makes sense. I'll probably edit runAtMinimumInterval
to do the same.
999dead
to
4e2e97d
Compare
This commit also includes adding more explicit tests for the return value of the functions.
4e2e97d
to
5614c49
Compare
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationsWe found no new type declarations in this PR Existing type declarationspackages/cli-kit/dist/private/node/conf-store.d.ts@@ -6,11 +6,13 @@ interface CacheValue<T> {
export type IntrospectionUrlKey = ;
export type PackageVersionKey = ;
type MostRecentOccurrenceKey = ;
+type RateLimitKey = ;
type ExportedKey = IntrospectionUrlKey | PackageVersionKey;
interface Cache {
[introspectionUrlKey: IntrospectionUrlKey]: CacheValue<string>;
[packageVersionKey: PackageVersionKey]: CacheValue<string>;
- [MostRecentOccurrenceKey: MostRecentOccurrenceKey]: CacheValue<boolean>;
+ [mostRecentOccurrenceKey: MostRecentOccurrenceKey]: CacheValue<boolean>;
+ [rateLimitKey: RateLimitKey]: CacheValue<number[]>;
}
export interface ConfSchema {
sessionStore: string;
@@ -63,7 +65,39 @@ interface TimeInterval {
* days, hours, minutes, and seconds properties.
* If the most recent occurrence is older than this, the task will be executed.
* @param task - The task to run if the most recent occurrence is older than the timeout.
- * @returns The result of the task, or undefined if the task was not run.
+ * @returns true if the task was run, or false if the task was not run.
*/
-export declare function runAtMinimumInterval(key: string, timeout: TimeInterval, task: () => Promise<void>, config?: LocalStorage<ConfSchema>): Promise<boolean | undefined>;
+export declare function runAtMinimumInterval(key: string, timeout: TimeInterval, task: () => Promise<void>, config?: LocalStorage<ConfSchema>): Promise<boolean>;
+interface RunWithRateLimitOptions {
+ /**
+ * The key to use for the cache.
+ */
+ key: string;
+ /**
+ * The number of times the task can be run within the limit
+ */
+ limit: number;
+ /**
+ * The window of time after which the rate limit is refreshed,
+ * expressed as an object with days, hours, minutes, and seconds properties.
+ * If the most recent occurrence is older than this, the task will be executed.
+ */
+ timeout: TimeInterval;
+ /**
+ * The task to run if the most recent occurrence is older than the timeout.
+ */
+ task: () => Promise<void>;
+}
+/**
+ * Execute a task with a time-based rate limit. The rate limit is enforced by
+ * checking how many times that task has been executed in a window of time ending
+ * at the current time. If the task has been executed more than the allowed number
+ * of times in that window, the task will not be executed.
+ *
+ * Note that this function has side effects, as it will also remove events prior
+ * to the window of time that is being checked.
+ * @param options - The options for the rate limiting.
+ * @returns true, or undefined if the task was not run.
+ */
+export declare function runWithRateLimit(options: RunWithRateLimitOptions, config?: LocalStorage<ConfSchema>): Promise<boolean>;
export {};
\ No newline at end of file
packages/cli-kit/dist/private/node/constants.d.ts@@ -53,4 +53,10 @@ export declare const pathConstants: {
export declare const sessionConstants: {
expirationTimeMarginInMinutes: number;
};
-export declare const bugsnagApiKey = "9e1e6889176fd0c795d5c659225e0fae";
\ No newline at end of file
+export declare const bugsnagApiKey = "9e1e6889176fd0c795d5c659225e0fae";
+export declare const reportingRateLimit: {
+ limit: number;
+ timeout: {
+ days: number;
+ };
+};
\ No newline at end of file
|
WHY are these changes introduced?
Some users are experiencing errors thousands of times daily, with minimal value added by analytics from those reports. We are throttling analytics (including bugsnag, monorail, and open analytics) to 300 events daily, to balance the need for continuous visibility into error conditions with the need to make those problems more visible by clearing out much of the noise from individual users with e.g. a background process retrying continuously thousands of times daily.
This isn't a perfect solution, we still are getting up to hundreds of reports daily from noisy users. But it's a quick and easy solution that gets us started down the path and eliminates the vast majority of noise immediately.
WHAT is this pull request doing?
Adds a rate limiter around the reporting functions. At the moment, there is no granularity for specific errors, though it would be great to add that in the future.
How to test your changes?
pnpm shopify app dev --path /path/to/your/app --config nonexistent --verbose
Skipping command analytics
with a payloadpackages/cli-kit/src/private/node
to have an absurdly low rate limit (e.g. once daily)Skipping command analytics due to rate limiting
with a payloadMeasuring impact
How do we know this change was effective? Please choose one:
Checklist