@redirhub/cloudflare-redirects 0.2.0 → 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.
- package/README.md +5 -4
- package/dist/core/report.d.ts +7 -4
- package/dist/core/report.js +57 -15
- package/dist/version.d.ts +2 -2
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -168,10 +168,11 @@ ignored: a Worker keeps no connection open.
|
|
|
168
168
|
|
|
169
169
|
**Reports.** A line queued while nothing is being sent goes out at once; lines queued while a
|
|
170
170
|
request is out go together in the next one (at most 100 lines or 64 KB, gzip with
|
|
171
|
-
`CompressionStream`, 2 second timeout)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
lines are dropped.
|
|
171
|
+
`CompressionStream`, 2 second timeout), and each full batch opens another request, up to four at
|
|
172
|
+
once. Only the request that starts sending waits for them, in `ctx.waitUntil`, for at most 10
|
|
173
|
+
seconds; the others only add their line, so no request waits on another's work. A failed or
|
|
174
|
+
rejected batch is dropped, never retried; beyond 10,000 waiting lines, lines are dropped. Dropped
|
|
175
|
+
lines are logged at once, then at most every ten minutes with the total by reason.
|
|
175
176
|
|
|
176
177
|
## Privacy
|
|
177
178
|
|
package/dist/core/report.d.ts
CHANGED
|
@@ -47,15 +47,18 @@ export interface ReporterOptions {
|
|
|
47
47
|
maxQueue?: number;
|
|
48
48
|
/** How long one invocation keeps sending before it leaves the rest to the next; default 10,000. */
|
|
49
49
|
budgetMs?: number;
|
|
50
|
+
/** Requests out at the same time; default 4. */
|
|
51
|
+
maxInFlight?: number;
|
|
50
52
|
}
|
|
51
53
|
/**
|
|
52
54
|
* Sends report lines without delaying a response and without a timer.
|
|
53
55
|
*
|
|
54
56
|
* A line queued while nothing is being sent goes out at once, so a site with little traffic
|
|
55
|
-
* reports each visit immediately and keeps nothing waiting. Lines queued while
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
57
|
+
* reports each visit immediately and keeps nothing waiting. Lines queued while requests are out
|
|
58
|
+
* go together in the next one (at most 100 lines, 64 KB, gzip), so a burst costs one request per
|
|
59
|
+
* hundred visits; up to `maxInFlight` (4) requests are out at once, so a busy instance keeps up.
|
|
60
|
+
* The invocation that starts sending keeps itself alive (through `keepAlive`) until the queue is
|
|
61
|
+
* empty and its requests are back; the others only add their line, so no request waits on
|
|
59
62
|
* another request's work. A failed or rejected batch is dropped and counted, never retried.
|
|
60
63
|
*/
|
|
61
64
|
export declare class Reporter {
|
package/dist/core/report.js
CHANGED
|
@@ -73,17 +73,25 @@ const DROP_LOG_EVERY_MS = 10 * 60_000;
|
|
|
73
73
|
* Sends report lines without delaying a response and without a timer.
|
|
74
74
|
*
|
|
75
75
|
* A line queued while nothing is being sent goes out at once, so a site with little traffic
|
|
76
|
-
* reports each visit immediately and keeps nothing waiting. Lines queued while
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
76
|
+
* reports each visit immediately and keeps nothing waiting. Lines queued while requests are out
|
|
77
|
+
* go together in the next one (at most 100 lines, 64 KB, gzip), so a burst costs one request per
|
|
78
|
+
* hundred visits; up to `maxInFlight` (4) requests are out at once, so a busy instance keeps up.
|
|
79
|
+
* The invocation that starts sending keeps itself alive (through `keepAlive`) until the queue is
|
|
80
|
+
* empty and its requests are back; the others only add their line, so no request waits on
|
|
80
81
|
* another request's work. A failed or rejected batch is dropped and counted, never retried.
|
|
81
82
|
*/
|
|
82
83
|
export class Reporter {
|
|
83
84
|
#options;
|
|
84
85
|
#queue = [];
|
|
86
|
+
/** The sender: the invocation that started it keeps it alive until it is done. */
|
|
85
87
|
#sending = null;
|
|
86
|
-
#
|
|
88
|
+
/** The sender's requests out now, and when it started (for the budget); #running once it has. */
|
|
89
|
+
#out = new Set();
|
|
90
|
+
#started = 0;
|
|
91
|
+
#running = false;
|
|
92
|
+
/** Lines dropped since the last log, by reason. */
|
|
93
|
+
#dropped = new Map();
|
|
94
|
+
#droppedSince = 0;
|
|
87
95
|
#lastDropLog = Number.NEGATIVE_INFINITY;
|
|
88
96
|
constructor(options) {
|
|
89
97
|
// `??` per field, not a spread: an option passed as undefined keeps its default.
|
|
@@ -95,6 +103,7 @@ export class Reporter {
|
|
|
95
103
|
timeoutMs: options.timeoutMs ?? 2000,
|
|
96
104
|
maxQueue: options.maxQueue ?? 10_000,
|
|
97
105
|
budgetMs: options.budgetMs ?? 10_000,
|
|
106
|
+
maxInFlight: options.maxInFlight ?? 4,
|
|
98
107
|
};
|
|
99
108
|
}
|
|
100
109
|
/** Queues one line; `keepAlive` holds the invocation open while this call has to send. */
|
|
@@ -102,6 +111,7 @@ export class Reporter {
|
|
|
102
111
|
if (this.#options.key === '') {
|
|
103
112
|
return;
|
|
104
113
|
}
|
|
114
|
+
this.#logDrops();
|
|
105
115
|
if (this.#queue.length >= this.#options.maxQueue) {
|
|
106
116
|
this.#drop(1, 'queue full');
|
|
107
117
|
return;
|
|
@@ -111,6 +121,9 @@ export class Reporter {
|
|
|
111
121
|
this.#sending = this.#send();
|
|
112
122
|
keepAlive?.(this.#sending);
|
|
113
123
|
}
|
|
124
|
+
else if (this.#running) {
|
|
125
|
+
this.#fill();
|
|
126
|
+
}
|
|
114
127
|
}
|
|
115
128
|
/** Resolves once everything queued so far has been sent or dropped. */
|
|
116
129
|
async flush() {
|
|
@@ -125,14 +138,31 @@ export class Reporter {
|
|
|
125
138
|
async #send() {
|
|
126
139
|
// Lines queued in the same turn of the event loop go in the first request.
|
|
127
140
|
await Promise.resolve();
|
|
128
|
-
|
|
141
|
+
this.#started = Date.now();
|
|
142
|
+
this.#running = true;
|
|
129
143
|
for (;;) {
|
|
130
|
-
|
|
144
|
+
this.#fill();
|
|
145
|
+
if (this.#out.size === 0) {
|
|
131
146
|
// Checked and cleared in one step: a line queued after this starts a new sender.
|
|
147
|
+
this.#running = false;
|
|
132
148
|
this.#sending = null;
|
|
133
149
|
return;
|
|
134
150
|
}
|
|
135
|
-
await
|
|
151
|
+
await Promise.race(this.#out);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Starts requests for the waiting lines, within the budget: one when none is out, and more,
|
|
156
|
+
* up to maxInFlight, only for full batches, so lines that arrive while a request is out wait
|
|
157
|
+
* for it and go together. Called by the sender and on each line queued while it runs.
|
|
158
|
+
*/
|
|
159
|
+
#fill() {
|
|
160
|
+
while (this.#out.size < this.#options.maxInFlight &&
|
|
161
|
+
this.#queue.length > 0 &&
|
|
162
|
+
(this.#out.size === 0 || this.#queue.length >= MAX_BATCH_LINES) &&
|
|
163
|
+
Date.now() - this.#started < this.#options.budgetMs) {
|
|
164
|
+
const post = this.#post(this.#take()).finally(() => this.#out.delete(post));
|
|
165
|
+
this.#out.add(post);
|
|
136
166
|
}
|
|
137
167
|
}
|
|
138
168
|
#take() {
|
|
@@ -180,15 +210,27 @@ export class Reporter {
|
|
|
180
210
|
this.#drop(lines.length, error instanceof Error ? error.name : 'error');
|
|
181
211
|
}
|
|
182
212
|
}
|
|
183
|
-
/** Counts dropped lines
|
|
213
|
+
/** Counts dropped lines by reason; see #logDrops. */
|
|
184
214
|
#drop(lines, reason) {
|
|
185
|
-
this.#dropped
|
|
186
|
-
|
|
187
|
-
|
|
215
|
+
if (this.#dropped.size === 0) {
|
|
216
|
+
this.#droppedSince = Date.now();
|
|
217
|
+
}
|
|
218
|
+
this.#dropped.set(reason, (this.#dropped.get(reason) ?? 0) + lines);
|
|
219
|
+
this.#logDrops();
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Logs the dropped lines: the first drop at once, then at most once per ten minutes with the
|
|
223
|
+
* total since the last log, by reason. Called on every drop and every line queued, so a count
|
|
224
|
+
* is logged even after the drops stop, without a timer.
|
|
225
|
+
*/
|
|
226
|
+
#logDrops() {
|
|
227
|
+
if (this.#dropped.size === 0 || Date.now() - this.#lastDropLog < DROP_LOG_EVERY_MS) {
|
|
188
228
|
return;
|
|
189
229
|
}
|
|
190
|
-
this.#
|
|
191
|
-
this.#dropped
|
|
192
|
-
this.#
|
|
230
|
+
const total = [...this.#dropped.values()].reduce((sum, n) => sum + n, 0);
|
|
231
|
+
const reasons = [...this.#dropped].map(([reason, n]) => `${reason} ${n}`).join(', ');
|
|
232
|
+
this.#options.logger?.warn(`${PACKAGE}: ${total} report lines dropped since ${new Date(this.#droppedSince).toISOString()} (${reasons})`);
|
|
233
|
+
this.#dropped.clear();
|
|
234
|
+
this.#lastDropLog = Date.now();
|
|
193
235
|
}
|
|
194
236
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** This package: names its log lines and the User-Agent of rules downloads and reports. */
|
|
2
2
|
export declare const PACKAGE = "cloudflare-redirects";
|
|
3
3
|
/** Equal to package.json's version (a test checks it). */
|
|
4
|
-
export declare const VERSION = "0.2.
|
|
5
|
-
export declare const USER_AGENT = "cloudflare-redirects/0.2.
|
|
4
|
+
export declare const VERSION = "0.2.1";
|
|
5
|
+
export declare const USER_AGENT = "cloudflare-redirects/0.2.1";
|
package/dist/version.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** This package: names its log lines and the User-Agent of rules downloads and reports. */
|
|
2
2
|
export const PACKAGE = 'cloudflare-redirects';
|
|
3
3
|
/** Equal to package.json's version (a test checks it). */
|
|
4
|
-
export const VERSION = '0.2.
|
|
4
|
+
export const VERSION = '0.2.1';
|
|
5
5
|
export const USER_AGENT = `${PACKAGE}/${VERSION}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redirhub/cloudflare-redirects",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Bulk redirects and 404 monitoring for Cloudflare Workers from a remote rules file: thousands of 301/302 redirects, short links and website migrations in front of your origin, live within a minute without a redeploy. RedirHub Integration Mode.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|