@convex-dev/sharded-counter 0.1.8 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +39 -41
  2. package/dist/{commonjs/client → client}/index.d.ts +24 -33
  3. package/dist/client/index.d.ts.map +1 -0
  4. package/dist/client/index.js.map +1 -0
  5. package/dist/component/_generated/api.d.ts +34 -0
  6. package/dist/component/_generated/api.d.ts.map +1 -0
  7. package/{src → dist}/component/_generated/api.js +11 -3
  8. package/dist/component/_generated/api.js.map +1 -0
  9. package/dist/component/_generated/component.d.ts +46 -0
  10. package/dist/component/_generated/component.d.ts.map +1 -0
  11. package/dist/component/_generated/component.js +11 -0
  12. package/dist/component/_generated/component.js.map +1 -0
  13. package/dist/component/_generated/dataModel.d.ts +46 -0
  14. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  15. package/dist/component/_generated/dataModel.js +11 -0
  16. package/dist/component/_generated/dataModel.js.map +1 -0
  17. package/{src → dist}/component/_generated/server.d.ts +10 -38
  18. package/dist/component/_generated/server.d.ts.map +1 -0
  19. package/dist/{esm/component → component}/_generated/server.js +9 -5
  20. package/dist/component/_generated/server.js.map +1 -0
  21. package/dist/component/convex.config.d.ts.map +1 -0
  22. package/dist/component/convex.config.js.map +1 -0
  23. package/dist/{commonjs/component → component}/public.d.ts +1 -1
  24. package/dist/component/public.d.ts.map +1 -0
  25. package/dist/{commonjs/component → component}/public.js +4 -4
  26. package/dist/component/public.js.map +1 -0
  27. package/dist/{commonjs/component → component}/schema.d.ts +2 -2
  28. package/dist/component/schema.d.ts.map +1 -0
  29. package/dist/component/schema.js.map +1 -0
  30. package/package.json +61 -38
  31. package/src/client/index.ts +45 -58
  32. package/src/component/_generated/api.ts +50 -0
  33. package/src/component/_generated/component.ts +63 -0
  34. package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +1 -1
  35. package/src/component/_generated/server.ts +156 -0
  36. package/src/component/counter.test.ts +12 -9
  37. package/src/component/public.ts +4 -4
  38. package/src/test.ts +18 -0
  39. package/dist/commonjs/client/index.d.ts.map +0 -1
  40. package/dist/commonjs/client/index.js.map +0 -1
  41. package/dist/commonjs/component/_generated/api.d.ts +0 -14
  42. package/dist/commonjs/component/_generated/api.d.ts.map +0 -1
  43. package/dist/commonjs/component/_generated/api.js +0 -22
  44. package/dist/commonjs/component/_generated/api.js.map +0 -1
  45. package/dist/commonjs/component/_generated/server.d.ts +0 -64
  46. package/dist/commonjs/component/_generated/server.d.ts.map +0 -1
  47. package/dist/commonjs/component/_generated/server.js +0 -74
  48. package/dist/commonjs/component/_generated/server.js.map +0 -1
  49. package/dist/commonjs/component/convex.config.d.ts.map +0 -1
  50. package/dist/commonjs/component/convex.config.js.map +0 -1
  51. package/dist/commonjs/component/public.d.ts.map +0 -1
  52. package/dist/commonjs/component/public.js.map +0 -1
  53. package/dist/commonjs/component/schema.d.ts.map +0 -1
  54. package/dist/commonjs/component/schema.js.map +0 -1
  55. package/dist/commonjs/package.json +0 -3
  56. package/dist/esm/client/index.d.ts +0 -178
  57. package/dist/esm/client/index.d.ts.map +0 -1
  58. package/dist/esm/client/index.js +0 -193
  59. package/dist/esm/client/index.js.map +0 -1
  60. package/dist/esm/component/_generated/api.d.ts +0 -14
  61. package/dist/esm/component/_generated/api.d.ts.map +0 -1
  62. package/dist/esm/component/_generated/api.js +0 -22
  63. package/dist/esm/component/_generated/api.js.map +0 -1
  64. package/dist/esm/component/_generated/server.d.ts +0 -64
  65. package/dist/esm/component/_generated/server.d.ts.map +0 -1
  66. package/dist/esm/component/_generated/server.js.map +0 -1
  67. package/dist/esm/component/convex.config.d.ts +0 -3
  68. package/dist/esm/component/convex.config.d.ts.map +0 -1
  69. package/dist/esm/component/convex.config.js +0 -3
  70. package/dist/esm/component/convex.config.js.map +0 -1
  71. package/dist/esm/component/public.d.ts +0 -23
  72. package/dist/esm/component/public.d.ts.map +0 -1
  73. package/dist/esm/component/public.js +0 -115
  74. package/dist/esm/component/public.js.map +0 -1
  75. package/dist/esm/component/schema.d.ts +0 -15
  76. package/dist/esm/component/schema.d.ts.map +0 -1
  77. package/dist/esm/component/schema.js +0 -10
  78. package/dist/esm/component/schema.js.map +0 -1
  79. package/dist/esm/package.json +0 -3
  80. package/src/component/_generated/api.d.ts +0 -67
  81. package/src/component/_generated/server.js +0 -90
  82. /package/dist/{commonjs/client → client}/index.js +0 -0
  83. /package/dist/{commonjs/component → component}/convex.config.d.ts +0 -0
  84. /package/dist/{commonjs/component → component}/convex.config.js +0 -0
  85. /package/dist/{commonjs/component → component}/schema.js +0 -0
@@ -1,178 +0,0 @@
1
- import { DocumentByName, Expand, FunctionReference, GenericDataModel, GenericMutationCtx, GenericQueryCtx, TableNamesInDataModel } from "convex/server";
2
- import { GenericId } from "convex/values";
3
- import { api } from "../component/_generated/api";
4
- /**
5
- * A sharded counter is a map from string -> counter, where each counter can
6
- * be incremented or decremented atomically.
7
- */
8
- export declare class ShardedCounter<ShardsKey extends string> {
9
- private component;
10
- /**
11
- * A sharded counter is a map from string -> counter, where each counter can
12
- * be incremented or decremented.
13
- *
14
- * The counter is sharded into multiple documents to allow for higher
15
- * throughput of updates. The default number of shards is 16.
16
- *
17
- * - More shards => higher throughput of updates.
18
- * - Fewer shards => lower latency when querying the counter.
19
- *
20
- * @param options.shards The number of shards for each counter, for fixed
21
- * keys.
22
- * @param options.defaultShards The number of shards for each counter, for
23
- * keys not in `options.shards`.
24
- */
25
- constructor(component: UseApi<typeof api>, options?: {
26
- shards?: Partial<Record<ShardsKey, number>>;
27
- defaultShards?: number;
28
- });
29
- private shardsForKey;
30
- private stickyShard;
31
- /**
32
- * Increase the counter for key `name` by `count`.
33
- * If `count` is negative, the counter will decrease.
34
- *
35
- * @param name The key to update the counter for.
36
- * @param count The amount to increment the counter by. Defaults to 1.
37
- */
38
- add<Name extends ShardsKey>(ctx: RunMutationCtx, name: Name, count?: number): Promise<void>;
39
- /**
40
- * Decrease the counter for key `name` by `count`.
41
- */
42
- subtract<Name extends ShardsKey>(ctx: RunMutationCtx, name: Name, count?: number): Promise<void>;
43
- /**
44
- * Increment the counter for key `name` by 1.
45
- */
46
- inc<Name extends ShardsKey>(ctx: RunMutationCtx, name: Name): Promise<void>;
47
- /**
48
- * Decrement the counter for key `name` by 1.
49
- */
50
- dec<Name extends ShardsKey>(ctx: RunMutationCtx, name: Name): Promise<void>;
51
- /**
52
- * Gets the counter for key `name`.
53
- *
54
- * NOTE: this reads from all shards. If used in a mutation, it will contend
55
- * with all mutations that update the counter for this key.
56
- */
57
- count<Name extends ShardsKey>(ctx: RunQueryCtx, name: Name): Promise<number>;
58
- /**
59
- * Redistribute counts evenly across the counter's shards.
60
- *
61
- * If there were more shards for this counter at some point, those shards
62
- * will be removed.
63
- *
64
- * If there were fewer shards for this counter, or if the random distribution
65
- * of counts is uneven, the counts will be redistributed evenly.
66
- *
67
- * This operation reads and writes all shards, so it can cause contention if
68
- * called too often.
69
- */
70
- rebalance<Name extends ShardsKey>(ctx: RunMutationCtx, name: Name): Promise<void>;
71
- /**
72
- * Clear the counter for key `name`.
73
- *
74
- * @param name The key to clear the counter for.
75
- */
76
- reset<Name extends ShardsKey>(ctx: RunMutationCtx, name: Name): Promise<void>;
77
- /**
78
- * Estimate the count of a counter by only reading from a subset of shards,
79
- * and extrapolating the total count.
80
- *
81
- * After a `rebalance`, or if there were a lot of data points to yield a
82
- * random distribution across shards, this should be a good approximation of
83
- * the total count. If there are few data points, which are not evenly
84
- * distributed across shards, this will be a poor approximation.
85
- *
86
- * Use this to reduce contention when reading the counter.
87
- */
88
- estimateCount<Name extends ShardsKey>(ctx: RunQueryCtx, name: Name, readFromShards?: number): Promise<number>;
89
- /**
90
- * Returns an object with methods to update and query the counter for key
91
- * `name`. For fixed keys, you can call `counter.for("<key>")` to get methods
92
- * for updating or querying the counter for that key. Example:
93
- *
94
- * ```ts
95
- * const counter = new ShardedCounter(components.shardedCounter);
96
- * const beanCounter = counter.for("beans");
97
- * export const pushPapers = mutation({
98
- * handler: async (ctx) => {
99
- * await beanCounter.inc(ctx);
100
- * },
101
- * });
102
- * ```
103
- */
104
- for<Name extends ShardsKey>(name: Name): {
105
- /**
106
- * Add `count` to the counter.
107
- */
108
- add: (ctx: RunMutationCtx, count?: number) => Promise<void>;
109
- /**
110
- * Subtract `count` from the counter.
111
- */
112
- subtract: (ctx: RunMutationCtx, count?: number) => Promise<void>;
113
- /**
114
- * Increment the counter by 1.
115
- */
116
- inc: (ctx: RunMutationCtx) => Promise<void>;
117
- /**
118
- * Decrement the counter by 1.
119
- */
120
- dec: (ctx: RunMutationCtx) => Promise<void>;
121
- /**
122
- * Get the current value of the counter.
123
- *
124
- * NOTE: this reads from all shards. If used in a mutation, it will
125
- * contend with all mutations that update the counter for this key.
126
- */
127
- count: (ctx: RunQueryCtx) => Promise<number>;
128
- /**
129
- * Reset the counter for this key.
130
- */
131
- reset: (ctx: RunMutationCtx) => Promise<void>;
132
- /**
133
- * Redistribute counts evenly across the counter's shards.
134
- *
135
- * This operation reads and writes all shards, so it can cause contention
136
- * if called too often.
137
- */
138
- rebalance: (ctx: RunMutationCtx) => Promise<void>;
139
- /**
140
- * Estimate the counter by only reading from a subset of shards,
141
- * and extrapolating the total count.
142
- *
143
- * Use this to reduce contention when reading the counter.
144
- */
145
- estimateCount: (ctx: RunQueryCtx, readFromShards?: number) => Promise<number>;
146
- };
147
- trigger<Ctx extends RunMutationCtx, Name extends ShardsKey>(name: Name): Trigger<Ctx, GenericDataModel, TableNamesInDataModel<GenericDataModel>>;
148
- }
149
- export type Trigger<Ctx, DataModel extends GenericDataModel, TableName extends TableNamesInDataModel<DataModel>> = (ctx: Ctx, change: Change<DataModel, TableName>) => Promise<void>;
150
- export type Change<DataModel extends GenericDataModel, TableName extends TableNamesInDataModel<DataModel>> = {
151
- id: GenericId<TableName>;
152
- } & ({
153
- operation: "insert";
154
- oldDoc: null;
155
- newDoc: DocumentByName<DataModel, TableName>;
156
- } | {
157
- operation: "update";
158
- oldDoc: DocumentByName<DataModel, TableName>;
159
- newDoc: DocumentByName<DataModel, TableName>;
160
- } | {
161
- operation: "delete";
162
- oldDoc: DocumentByName<DataModel, TableName>;
163
- newDoc: null;
164
- });
165
- type RunQueryCtx = {
166
- runQuery: GenericQueryCtx<GenericDataModel>["runQuery"];
167
- };
168
- type RunMutationCtx = {
169
- runMutation: GenericMutationCtx<GenericDataModel>["runMutation"];
170
- };
171
- export type OpaqueIds<T> = T extends GenericId<infer _T> ? string : T extends (infer U)[] ? OpaqueIds<U>[] : T extends object ? {
172
- [K in keyof T]: OpaqueIds<T[K]>;
173
- } : T;
174
- export type UseApi<API> = Expand<{
175
- [mod in keyof API]: API[mod] extends FunctionReference<infer FType, "public", infer FArgs, infer FReturnType, infer FComponentPath> ? FunctionReference<FType, "internal", OpaqueIds<FArgs>, OpaqueIds<FReturnType>, FComponentPath> : UseApi<API[mod]>;
176
- }>;
177
- export {};
178
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"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"}
@@ -1,193 +0,0 @@
1
- /**
2
- * A sharded counter is a map from string -> counter, where each counter can
3
- * be incremented or decremented atomically.
4
- */
5
- export class ShardedCounter {
6
- component;
7
- /**
8
- * A sharded counter is a map from string -> counter, where each counter can
9
- * be incremented or decremented.
10
- *
11
- * The counter is sharded into multiple documents to allow for higher
12
- * throughput of updates. The default number of shards is 16.
13
- *
14
- * - More shards => higher throughput of updates.
15
- * - Fewer shards => lower latency when querying the counter.
16
- *
17
- * @param options.shards The number of shards for each counter, for fixed
18
- * keys.
19
- * @param options.defaultShards The number of shards for each counter, for
20
- * keys not in `options.shards`.
21
- */
22
- constructor(component, options) {
23
- this.component = component;
24
- this.stickyShard = {};
25
- const defaultShards = options?.defaultShards;
26
- this.shardsForKey = (name) => {
27
- const explicitShards = options?.shards?.[name];
28
- return explicitShards ?? defaultShards;
29
- };
30
- }
31
- shardsForKey;
32
- // Keep track of the shard for each key, so multiple mutations on the same key
33
- // will use the same shard.
34
- stickyShard;
35
- /**
36
- * Increase the counter for key `name` by `count`.
37
- * If `count` is negative, the counter will decrease.
38
- *
39
- * @param name The key to update the counter for.
40
- * @param count The amount to increment the counter by. Defaults to 1.
41
- */
42
- async add(ctx, name, count = 1) {
43
- const shard = await ctx.runMutation(this.component.public.add, {
44
- name,
45
- count,
46
- shard: this.stickyShard?.[name],
47
- shards: this.shardsForKey(name),
48
- });
49
- this.stickyShard[name] = shard;
50
- }
51
- /**
52
- * Decrease the counter for key `name` by `count`.
53
- */
54
- async subtract(ctx, name, count = 1) {
55
- return this.add(ctx, name, -count);
56
- }
57
- /**
58
- * Increment the counter for key `name` by 1.
59
- */
60
- async inc(ctx, name) {
61
- return this.add(ctx, name, 1);
62
- }
63
- /**
64
- * Decrement the counter for key `name` by 1.
65
- */
66
- async dec(ctx, name) {
67
- return this.add(ctx, name, -1);
68
- }
69
- /**
70
- * Gets the counter for key `name`.
71
- *
72
- * NOTE: this reads from all shards. If used in a mutation, it will contend
73
- * with all mutations that update the counter for this key.
74
- */
75
- async count(ctx, name) {
76
- return ctx.runQuery(this.component.public.count, { name });
77
- }
78
- /**
79
- * Redistribute counts evenly across the counter's shards.
80
- *
81
- * If there were more shards for this counter at some point, those shards
82
- * will be removed.
83
- *
84
- * If there were fewer shards for this counter, or if the random distribution
85
- * of counts is uneven, the counts will be redistributed evenly.
86
- *
87
- * This operation reads and writes all shards, so it can cause contention if
88
- * called too often.
89
- */
90
- async rebalance(ctx, name) {
91
- await ctx.runMutation(this.component.public.rebalance, {
92
- name,
93
- shards: this.shardsForKey(name),
94
- });
95
- }
96
- /**
97
- * Clear the counter for key `name`.
98
- *
99
- * @param name The key to clear the counter for.
100
- */
101
- async reset(ctx, name) {
102
- await ctx.runMutation(this.component.public.reset, { name });
103
- }
104
- /**
105
- * Estimate the count of a counter by only reading from a subset of shards,
106
- * and extrapolating the total count.
107
- *
108
- * After a `rebalance`, or if there were a lot of data points to yield a
109
- * random distribution across shards, this should be a good approximation of
110
- * the total count. If there are few data points, which are not evenly
111
- * distributed across shards, this will be a poor approximation.
112
- *
113
- * Use this to reduce contention when reading the counter.
114
- */
115
- async estimateCount(ctx, name, readFromShards = 1) {
116
- return await ctx.runQuery(this.component.public.estimateCount, {
117
- name,
118
- shards: this.shardsForKey(name),
119
- readFromShards,
120
- });
121
- }
122
- /**
123
- * Returns an object with methods to update and query the counter for key
124
- * `name`. For fixed keys, you can call `counter.for("<key>")` to get methods
125
- * for updating or querying the counter for that key. Example:
126
- *
127
- * ```ts
128
- * const counter = new ShardedCounter(components.shardedCounter);
129
- * const beanCounter = counter.for("beans");
130
- * export const pushPapers = mutation({
131
- * handler: async (ctx) => {
132
- * await beanCounter.inc(ctx);
133
- * },
134
- * });
135
- * ```
136
- */
137
- for(name) {
138
- return {
139
- /**
140
- * Add `count` to the counter.
141
- */
142
- add: async (ctx, count = 1) => this.add(ctx, name, count),
143
- /**
144
- * Subtract `count` from the counter.
145
- */
146
- subtract: async (ctx, count = 1) => this.add(ctx, name, -count),
147
- /**
148
- * Increment the counter by 1.
149
- */
150
- inc: async (ctx) => this.add(ctx, name, 1),
151
- /**
152
- * Decrement the counter by 1.
153
- */
154
- dec: async (ctx) => this.add(ctx, name, -1),
155
- /**
156
- * Get the current value of the counter.
157
- *
158
- * NOTE: this reads from all shards. If used in a mutation, it will
159
- * contend with all mutations that update the counter for this key.
160
- */
161
- count: async (ctx) => this.count(ctx, name),
162
- /**
163
- * Reset the counter for this key.
164
- */
165
- reset: async (ctx) => this.reset(ctx, name),
166
- /**
167
- * Redistribute counts evenly across the counter's shards.
168
- *
169
- * This operation reads and writes all shards, so it can cause contention
170
- * if called too often.
171
- */
172
- rebalance: async (ctx) => this.rebalance(ctx, name),
173
- /**
174
- * Estimate the counter by only reading from a subset of shards,
175
- * and extrapolating the total count.
176
- *
177
- * Use this to reduce contention when reading the counter.
178
- */
179
- estimateCount: async (ctx, readFromShards = 1) => this.estimateCount(ctx, name, readFromShards),
180
- };
181
- }
182
- trigger(name) {
183
- return async (ctx, change) => {
184
- if (change.operation === "insert") {
185
- await this.inc(ctx, name);
186
- }
187
- else if (change.operation === "delete") {
188
- await this.dec(ctx, name);
189
- }
190
- };
191
- }
192
- }
193
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAYA;;;GAGG;AACH,MAAM,OAAO,cAAc;IAiBf;IAhBV;;;;;;;;;;;;;;OAcG;IACH,YACU,SAA6B,EACrC,OAGC;QAJO,cAAS,GAAT,SAAS,CAAoB;QAMrC,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QACtB,MAAM,aAAa,GAAG,OAAO,EAAE,aAAa,CAAC;QAC7C,IAAI,CAAC,YAAY,GAAG,CAAC,IAAe,EAAE,EAAE;YACtC,MAAM,cAAc,GAAG,OAAO,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;YAC/C,OAAO,cAAc,IAAI,aAAa,CAAC;QACzC,CAAC,CAAC;IACJ,CAAC;IAEO,YAAY,CAA0C;IAE9D,8EAA8E;IAC9E,2BAA2B;IACnB,WAAW,CAAyB;IAE5C;;;;;;OAMG;IACH,KAAK,CAAC,GAAG,CACP,GAAmB,EACnB,IAAU,EACV,QAAgB,CAAC;QAEjB,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE;YAC7D,IAAI;YACJ,KAAK;YACL,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC;YAC/B,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;SAChC,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ,CACZ,GAAmB,EACnB,IAAU,EACV,QAAgB,CAAC;QAEjB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG,CAAyB,GAAmB,EAAE,IAAU;QAC/D,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG,CAAyB,GAAmB,EAAE,IAAU;QAC/D,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,KAAK,CAAyB,GAAgB,EAAE,IAAU;QAC9D,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,SAAS,CAAyB,GAAmB,EAAE,IAAU;QACrE,MAAM,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,EAAE;YACrD,IAAI;YACJ,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;SAChC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,KAAK,CAAyB,GAAmB,EAAE,IAAU;QACjE,MAAM,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,aAAa,CACjB,GAAgB,EAChB,IAAU,EACV,iBAAyB,CAAC;QAE1B,OAAO,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,aAAa,EAAE;YAC7D,IAAI;YACJ,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;YAC/B,cAAc;SACf,CAAC,CAAC;IACL,CAAC;IACD;;;;;;;;;;;;;;OAcG;IACH,GAAG,CAAyB,IAAU;QACpC,OAAO;YACL;;eAEG;YACH,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,QAAgB,CAAC,EAAE,EAAE,CACpD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC;YAC5B;;eAEG;YACH,QAAQ,EAAE,KAAK,EAAE,GAAmB,EAAE,QAAgB,CAAC,EAAE,EAAE,CACzD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC;YAC7B;;eAEG;YACH,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D;;eAEG;YACH,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC3D;;;;;eAKG;YACH,KAAK,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC;YACxD;;eAEG;YACH,KAAK,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC;YAC3D;;;;;eAKG;YACH,SAAS,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC;YACnE;;;;;eAKG;YACH,aAAa,EAAE,KAAK,EAAE,GAAgB,EAAE,iBAAyB,CAAC,EAAE,EAAE,CACpE,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,cAAc,CAAC;SAChD,CAAC;IACJ,CAAC;IACD,OAAO,CACL,IAAU;QAEV,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE;YAC3B,IAAI,MAAM,CAAC,SAAS,KAAK,QAAQ,EAAE;gBACjC,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;aAC3B;iBAAM,IAAI,MAAM,CAAC,SAAS,KAAK,QAAQ,EAAE;gBACxC,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;aAC3B;QACH,CAAC,CAAC;IACJ,CAAC;CACF"}
@@ -1,14 +0,0 @@
1
- /**
2
- * A utility for referencing Convex functions in your app's API.
3
- *
4
- * Usage:
5
- * ```js
6
- * const myFunctionReference = api.myModule.myFunction;
7
- * ```
8
- */
9
- export const api: import("convex/server").AnyApi;
10
- export const internal: import("convex/server").AnyApi;
11
- export const components: {
12
- [x: string]: import("../../../node_modules/convex/dist/esm-types/server/components").AnyComponentReference;
13
- };
14
- //# sourceMappingURL=api.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/component/_generated/api.js"],"names":[],"mappings":"AAYA;;;;;;;GAOG;AACH,iDAA0B;AAC1B,sDAA+B;AAC/B;;EAA8C"}
@@ -1,22 +0,0 @@
1
- /* eslint-disable */
2
- /**
3
- * Generated `api` utility.
4
- *
5
- * THIS CODE IS AUTOMATICALLY GENERATED.
6
- *
7
- * To regenerate, run `npx convex dev`.
8
- * @module
9
- */
10
- import { anyApi, componentsGeneric } from "convex/server";
11
- /**
12
- * A utility for referencing Convex functions in your app's API.
13
- *
14
- * Usage:
15
- * ```js
16
- * const myFunctionReference = api.myModule.myFunction;
17
- * ```
18
- */
19
- export const api = anyApi;
20
- export const internal = anyApi;
21
- export const components = componentsGeneric();
22
- //# sourceMappingURL=api.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../../src/component/_generated/api.js"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,MAAM,CAAC;AAC1B,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC;AAC/B,MAAM,CAAC,MAAM,UAAU,GAAG,iBAAiB,EAAE,CAAC"}
@@ -1,64 +0,0 @@
1
- /**
2
- * Define a query in this Convex app's public API.
3
- *
4
- * This function will be allowed to read your Convex database and will be accessible from the client.
5
- *
6
- * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
7
- * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
8
- */
9
- export const query: import("convex/server").QueryBuilder<any, "public">;
10
- /**
11
- * Define a query that is only accessible from other Convex functions (but not from the client).
12
- *
13
- * This function will be allowed to read from your Convex database. It will not be accessible from the client.
14
- *
15
- * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
16
- * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
17
- */
18
- export const internalQuery: import("convex/server").QueryBuilder<any, "internal">;
19
- /**
20
- * Define a mutation in this Convex app's public API.
21
- *
22
- * This function will be allowed to modify your Convex database and will be accessible from the client.
23
- *
24
- * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
25
- * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
26
- */
27
- export const mutation: import("convex/server").MutationBuilder<any, "public">;
28
- /**
29
- * Define a mutation that is only accessible from other Convex functions (but not from the client).
30
- *
31
- * This function will be allowed to modify your Convex database. It will not be accessible from the client.
32
- *
33
- * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
34
- * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
35
- */
36
- export const internalMutation: import("convex/server").MutationBuilder<any, "internal">;
37
- /**
38
- * Define an action in this Convex app's public API.
39
- *
40
- * An action is a function which can execute any JavaScript code, including non-deterministic
41
- * code and code with side-effects, like calling third-party services.
42
- * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
43
- * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
44
- *
45
- * @param func - The action. It receives an {@link ActionCtx} as its first argument.
46
- * @returns The wrapped action. Include this as an `export` to name it and make it accessible.
47
- */
48
- export const action: import("convex/server").ActionBuilder<any, "public">;
49
- /**
50
- * Define an action that is only accessible from other Convex functions (but not from the client).
51
- *
52
- * @param func - The function. It receives an {@link ActionCtx} as its first argument.
53
- * @returns The wrapped function. Include this as an `export` to name it and make it accessible.
54
- */
55
- export const internalAction: import("convex/server").ActionBuilder<any, "internal">;
56
- /**
57
- * Define a Convex HTTP action.
58
- *
59
- * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
60
- * as its second.
61
- * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
62
- */
63
- export const httpAction: (func: (ctx: import("convex/server").GenericActionCtx<import("convex/server").GenericDataModel>, request: Request) => Promise<Response>) => import("convex/server").PublicHttpAction;
64
- //# sourceMappingURL=server.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../../src/component/_generated/server.js"],"names":[],"mappings":"AAqBA;;;;;;;GAOG;AACH,wEAAkC;AAElC;;;;;;;GAOG;AACH,kFAAkD;AAElD;;;;;;;GAOG;AACH,8EAAwC;AAExC;;;;;;;GAOG;AACH,wFAAwD;AAExD;;;;;;;;;;GAUG;AACH,0EAAoC;AAEpC;;;;;GAKG;AACH,oFAAoD;AAEpD;;;;;;GAMG;AACH,8MAA4C"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../../src/component/_generated/server.js"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG;AAEH,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,YAAY,CAAC;AAElC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,oBAAoB,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,eAAe,CAAC;AAExC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,uBAAuB,CAAC;AAExD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,aAAa,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,qBAAqB,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,iBAAiB,CAAC"}
@@ -1,3 +0,0 @@
1
- declare const _default: import("convex/server").ComponentDefinition<any>;
2
- export default _default;
3
- //# sourceMappingURL=convex.config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"convex.config.d.ts","sourceRoot":"","sources":["../../../src/component/convex.config.ts"],"names":[],"mappings":";AAEA,wBAAiD"}
@@ -1,3 +0,0 @@
1
- import { defineComponent } from "convex/server";
2
- export default defineComponent("shardedCounter");
3
- //# sourceMappingURL=convex.config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"convex.config.js","sourceRoot":"","sources":["../../../src/component/convex.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,eAAe,eAAe,CAAC,gBAAgB,CAAC,CAAC"}
@@ -1,23 +0,0 @@
1
- export declare const DEFAULT_SHARD_COUNT = 16;
2
- export declare const add: import("convex/server").RegisteredMutation<"public", {
3
- shard?: number | undefined;
4
- shards?: number | undefined;
5
- name: string;
6
- count: number;
7
- }, Promise<number>>;
8
- export declare const count: import("convex/server").RegisteredQuery<"public", {
9
- name: string;
10
- }, Promise<number>>;
11
- export declare const rebalance: import("convex/server").RegisteredMutation<"public", {
12
- shards?: number | undefined;
13
- name: string;
14
- }, Promise<void>>;
15
- export declare const reset: import("convex/server").RegisteredMutation<"public", {
16
- name: string;
17
- }, Promise<void>>;
18
- export declare const estimateCount: import("convex/server").RegisteredQuery<"public", {
19
- shards?: number | undefined;
20
- readFromShards?: number | undefined;
21
- name: string;
22
- }, Promise<number>>;
23
- //# sourceMappingURL=public.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/component/public.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,eAAO,MAAM,GAAG;;;;;mBA6Bd,CAAC;AAEH,eAAO,MAAM,KAAK;;mBAUhB,CAAC;AAEH,eAAO,MAAM,SAAS;;;iBA2BpB,CAAC;AAEH,eAAO,MAAM,KAAK;;iBAWhB,CAAC;AAEH,eAAO,MAAM,aAAa;;;;mBA2BxB,CAAC"}
@@ -1,115 +0,0 @@
1
- import { v } from "convex/values";
2
- import { mutation, query } from "./_generated/server";
3
- export const DEFAULT_SHARD_COUNT = 16;
4
- export const add = mutation({
5
- args: {
6
- name: v.string(),
7
- count: v.number(),
8
- shard: v.optional(v.number()),
9
- shards: v.optional(v.number()),
10
- },
11
- returns: v.number(),
12
- handler: async (ctx, args) => {
13
- const shard = args.shard ??
14
- Math.floor(Math.random() * (args.shards ?? DEFAULT_SHARD_COUNT));
15
- const counter = await ctx.db
16
- .query("counters")
17
- .withIndex("name", (q) => q.eq("name", args.name).eq("shard", shard))
18
- .unique();
19
- if (counter) {
20
- await ctx.db.patch(counter._id, {
21
- value: counter.value + args.count,
22
- });
23
- }
24
- else {
25
- await ctx.db.insert("counters", {
26
- name: args.name,
27
- value: args.count,
28
- shard,
29
- });
30
- }
31
- return shard;
32
- },
33
- });
34
- export const count = query({
35
- args: { name: v.string() },
36
- returns: v.number(),
37
- handler: async (ctx, args) => {
38
- const counters = await ctx.db
39
- .query("counters")
40
- .withIndex("name", (q) => q.eq("name", args.name))
41
- .collect();
42
- return counters.reduce((sum, counter) => sum + counter.value, 0);
43
- },
44
- });
45
- export const rebalance = mutation({
46
- args: { name: v.string(), shards: v.optional(v.number()) },
47
- handler: async (ctx, args) => {
48
- const counters = await ctx.db
49
- .query("counters")
50
- .withIndex("name", (q) => q.eq("name", args.name))
51
- .collect();
52
- const count = counters.reduce((sum, counter) => sum + counter.value, 0);
53
- const shardCount = args.shards ?? DEFAULT_SHARD_COUNT;
54
- const value = count / shardCount;
55
- for (let i = 0; i < shardCount; i++) {
56
- const shard = counters.find((c) => c.shard === i);
57
- if (shard) {
58
- await ctx.db.patch(shard._id, { value });
59
- }
60
- else {
61
- await ctx.db.insert("counters", {
62
- name: args.name,
63
- value,
64
- shard: i,
65
- });
66
- }
67
- }
68
- const toDelete = counters.filter((c) => c.shard >= shardCount);
69
- for (const counter of toDelete) {
70
- await ctx.db.delete(counter._id);
71
- }
72
- },
73
- });
74
- export const reset = mutation({
75
- args: { name: v.string() },
76
- handler: async (ctx, args) => {
77
- await ctx.db
78
- .query("counters")
79
- .withIndex("name", (q) => q.eq("name", args.name))
80
- .collect()
81
- .then((counters) => Promise.all(counters.map((c) => ctx.db.delete(c._id))));
82
- },
83
- });
84
- export const estimateCount = query({
85
- args: {
86
- name: v.string(),
87
- readFromShards: v.optional(v.number()),
88
- shards: v.optional(v.number()),
89
- },
90
- handler: async (ctx, args) => {
91
- const shardCount = args.shards ?? DEFAULT_SHARD_COUNT;
92
- const readFromShards = Math.min(Math.max(1, args.readFromShards ?? 1), shardCount);
93
- const shards = shuffle(Array.from({ length: shardCount }, (_, i) => i)).slice(0, readFromShards);
94
- let readCount = 0;
95
- for (const shard of shards) {
96
- const counter = await ctx.db
97
- .query("counters")
98
- .withIndex("name", (q) => q.eq("name", args.name).eq("shard", shard))
99
- .unique();
100
- if (counter) {
101
- readCount += counter.value;
102
- }
103
- }
104
- return (readCount * shardCount) / readFromShards;
105
- },
106
- });
107
- // Fisher-Yates shuffle
108
- function shuffle(array) {
109
- for (let i = array.length - 1; i > 0; i--) {
110
- const j = Math.floor(Math.random() * (i + 1));
111
- [array[i], array[j]] = [array[j], array[i]];
112
- }
113
- return array;
114
- }
115
- //# sourceMappingURL=public.js.map