tangled
alpha
login
or
join now
mary.my.id
/
boat
22
fork
atom
handy online tools for AT Protocol
boat.kelinci.net
atproto
bluesky
atcute
typescript
solidjs
22
fork
atom
overview
issues
pulls
pipelines
fix: wait for ratelimit on error
mary.my.id
1 year ago
e62aa970
13a90873
verified
This commit was signed with the committer's
known signature
.
mary.my.id
SSH Key Fingerprint:
SHA256:ZlTP/auFSGpGnaoDg4mCTG1g9OZvXp62jWR4c6H4O3c=
+67
-41
1 changed file
expand all
collapse all
unified
split
src
views
bluesky
threadgate-applicator
steps
step4_confirmation.tsx
+67
-41
src/views/bluesky/threadgate-applicator/steps/step4_confirmation.tsx
···
1
1
-
import { createSignal } from 'solid-js';
1
1
+
import { createSignal, Show } from 'solid-js';
2
2
3
3
-
import { HeadersObject, XRPC } from '@atcute/client';
3
3
+
import { XRPC, XRPCError } from '@atcute/client';
4
4
import { AppBskyFeedThreadgate, ComAtprotoRepoApplyWrites } from '@atcute/client/lexicons';
5
5
import { chunked } from '@mary/array-fns';
6
6
···
12
12
import { Stage, StageActions, StageErrorView, WizardStepProps } from '~/components/wizard';
13
13
14
14
import { parseAtUri } from '~/api/utils/strings';
15
15
+
import Logger, { createLogger } from '~/components/logger';
15
16
import { ThreadgateApplicatorConstraints } from '../page';
16
17
17
18
const Step4_Confirmation = ({
···
22
23
}: WizardStepProps<ThreadgateApplicatorConstraints, 'Step4_Confirmation'>) => {
23
24
const [checked, setChecked] = createSignal(false);
24
25
25
25
-
const [status, setStatus] = createSignal<string>();
26
26
const [error, setError] = createSignal<string>();
27
27
28
28
+
const [isLoggerVisible, setIsLoggerVisible] = createSignal(false);
29
29
+
const logger = createLogger();
30
30
+
28
31
const mutation = createMutation({
29
32
async mutationFn() {
30
30
-
setStatus(`Preparing records`);
33
33
+
logger.log(`Preparing writes`);
31
34
32
35
const rules = data.rules;
33
36
const writes: ComAtprotoRepoApplyWrites.Input['writes'] = [];
···
83
86
}
84
87
}
85
88
89
89
+
logger.log(`${writes.length} write operations to apply`);
90
90
+
86
91
const did = data.profile.didDoc.id;
87
92
const rpc = new XRPC({ handler: data.manager });
88
93
89
89
-
const total = writes.length;
90
90
-
let written = 0;
91
91
-
for (const chunk of chunked(writes, 200)) {
92
92
-
setStatus(`Writing records (${written}/${total})`);
94
94
+
const RATELIMIT_POINT_LIMIT = 150 * 3;
93
95
94
94
-
const { headers } = await rpc.call('com.atproto.repo.applyWrites', {
95
95
-
data: {
96
96
-
repo: did,
97
97
-
writes: chunk,
98
98
-
},
99
99
-
});
96
96
+
{
97
97
+
using progress = logger.progress(`Applying writes`);
100
98
101
101
-
written += chunk.length;
99
99
+
let written = 0;
100
100
+
for (const chunk of chunked(writes, 200)) {
101
101
+
try {
102
102
+
const { headers } = await rpc.call('com.atproto.repo.applyWrites', {
103
103
+
data: {
104
104
+
repo: did,
105
105
+
writes: chunk,
106
106
+
},
107
107
+
});
102
108
103
103
-
await waitForRatelimit(headers, 150 * 3);
109
109
+
written += chunk.length;
110
110
+
progress.update(`Applying writes (${written} applied)`);
111
111
+
112
112
+
if ('ratelimit-remaining' in headers) {
113
113
+
const remaining = +headers['ratelimit-remaining'];
114
114
+
const reset = +headers['ratelimit-reset'] * 1_000;
115
115
+
116
116
+
if (remaining < RATELIMIT_POINT_LIMIT) {
117
117
+
// add some delay to be sure
118
118
+
const delta = reset - Date.now() + 5_000;
119
119
+
using _progress = logger.progress(`Reached ratelimit, waiting ${delta}ms`);
120
120
+
121
121
+
await new Promise((resolve) => setTimeout(resolve, delta));
122
122
+
}
123
123
+
}
124
124
+
} catch (err) {
125
125
+
if (!(err instanceof XRPCError) || err.kind !== 'RateLimitExceeded') {
126
126
+
throw err;
127
127
+
}
128
128
+
129
129
+
const headers = err.headers;
130
130
+
if ('ratelimit-remaining' in headers) {
131
131
+
const remaining = +headers['ratelimit-remaining'];
132
132
+
const reset = +headers['ratelimit-reset'] * 1_000;
133
133
+
134
134
+
if (remaining < RATELIMIT_POINT_LIMIT) {
135
135
+
// add some delay to be sure
136
136
+
const delta = reset - Date.now() + 5_000;
137
137
+
using _progress = logger.progress(`Reached ratelimit, waiting ${delta}ms`);
138
138
+
139
139
+
await new Promise((resolve) => setTimeout(resolve, delta));
140
140
+
}
141
141
+
} else {
142
142
+
using _progress = logger.progress(`Reached ratelimit, waiting 1 minute`);
143
143
+
144
144
+
await new Promise((resolve) => setTimeout(resolve, 60 * 1_000));
145
145
+
}
146
146
+
}
147
147
+
}
104
148
}
105
149
},
106
150
onMutate() {
107
151
setError();
108
108
-
},
109
109
-
onSettled() {
110
110
-
setStatus();
152
152
+
setIsLoggerVisible(true);
111
153
},
112
154
onSuccess() {
155
155
+
logger.log(`All writes applied`);
113
156
onNext('Step5_Finished', {});
114
157
},
115
158
onError(error) {
116
159
let message: string | undefined;
117
160
118
161
if (message !== undefined) {
119
119
-
setError(message);
162
162
+
logger.error(message);
120
163
} else {
121
164
console.error(error);
122
122
-
setError(`Something went wrong: ${error}`);
165
165
+
logger.error(`Something went wrong:\n${error}`);
123
166
}
124
167
},
125
168
});
···
139
182
140
183
<ToggleInput label="I understand" required checked={checked()} onChange={setChecked} />
141
184
142
142
-
<div
143
143
-
hidden={status() === undefined}
144
144
-
class="whitespace-pre-wrap text-[0.8125rem] font-medium leading-5 text-gray-500"
145
145
-
>
146
146
-
{status()}
147
147
-
</div>
185
185
+
<Show when={isLoggerVisible()}>
186
186
+
<Logger logger={logger} />
187
187
+
</Show>
148
188
149
189
<StageErrorView error={error()} />
150
190
···
161
201
};
162
202
163
203
export default Step4_Confirmation;
164
164
-
165
165
-
const waitForRatelimit = async (headers: HeadersObject, expected: number) => {
166
166
-
if ('ratelimit-remaining' in headers) {
167
167
-
const remaining = +headers['ratelimit-remaining'];
168
168
-
const reset = +headers['ratelimit-reset'] * 1_000;
169
169
-
170
170
-
if (remaining < expected) {
171
171
-
// add some delay to be sure
172
172
-
const delta = reset - Date.now() + 5_000;
173
173
-
174
174
-
await new Promise((resolve) => setTimeout(resolve, delta));
175
175
-
}
176
176
-
}
177
177
-
};