braidfs 0.0.154 → 0.0.155
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/index.js +111 -51
- package/index.sh +6 -4
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -541,8 +541,6 @@ function sync_url(url) {
|
|
|
541
541
|
var res = await braid_fetch(url, {
|
|
542
542
|
signal: self.ac.signal,
|
|
543
543
|
method: 'HEAD',
|
|
544
|
-
// version needed to force Merge-Type return header
|
|
545
|
-
version: [],
|
|
546
544
|
// setting subscribe shouldn't work according to spec,
|
|
547
545
|
// but it does for some old braid-text servers
|
|
548
546
|
subscribe: !!try_sub,
|
|
@@ -565,15 +563,16 @@ function sync_url(url) {
|
|
|
565
563
|
}
|
|
566
564
|
}
|
|
567
565
|
} catch (e) {
|
|
568
|
-
if (
|
|
569
|
-
}
|
|
570
|
-
if (self.ac.signal.aborted) return
|
|
566
|
+
if (self.ac.signal.aborted) return
|
|
571
567
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
568
|
+
// Retry with increasing delays: 1s, 2s, 3s, 3s, 3s...
|
|
569
|
+
var delay = Math.min(++retry_count, 3)
|
|
570
|
+
console.log(`retrying in ${delay}s: ${url} after error: ${e}`)
|
|
571
|
+
await new Promise(r => setTimeout(r, delay * 1000))
|
|
572
|
+
if (self.ac.signal.aborted) return
|
|
573
|
+
continue
|
|
574
|
+
}
|
|
575
|
+
throw new Error(`no merge-type detected for url: ${url}`)
|
|
577
576
|
}
|
|
578
577
|
}
|
|
579
578
|
|
|
@@ -1091,6 +1090,22 @@ function sync_url(url) {
|
|
|
1091
1090
|
}
|
|
1092
1091
|
})
|
|
1093
1092
|
|
|
1093
|
+
// Debugging: Print out changes in memory usage
|
|
1094
|
+
;(async () => {
|
|
1095
|
+
var before = process.memoryUsage().heapUsed
|
|
1096
|
+
try {
|
|
1097
|
+
var r = await braid_text.get(url, { full_response: true })
|
|
1098
|
+
if (self.ac.signal.aborted) return
|
|
1099
|
+
var after = process.memoryUsage().heapUsed
|
|
1100
|
+
console.log(`${url}: +${((after - before) / 1e6).toFixed(1)} MB`
|
|
1101
|
+
+ ` (heap now ${(after / 1e6).toFixed(0)} MB,`
|
|
1102
|
+
+ ` body ${(r?.body?.length || 0)} chars)`)
|
|
1103
|
+
} catch (e) {
|
|
1104
|
+
if (e?.name !== 'AbortError')
|
|
1105
|
+
console.log(`DOC-MEM ${url}: probe error ${e}`)
|
|
1106
|
+
}
|
|
1107
|
+
})()
|
|
1108
|
+
|
|
1094
1109
|
// Use braid_text.sync for bidirectional sync with the remote URL
|
|
1095
1110
|
if (is_external_link) braid_text.sync(url, new URL(url), {
|
|
1096
1111
|
|
|
@@ -1174,81 +1189,126 @@ async function ensure_path(path) {
|
|
|
1174
1189
|
}
|
|
1175
1190
|
}
|
|
1176
1191
|
|
|
1192
|
+
// Rate Limiting Behavior:
|
|
1193
|
+
//
|
|
1194
|
+
// - If nothing is connected (totally offline), then give one turn per 3s
|
|
1195
|
+
// - Pick first host, and resolve first ticket for that host
|
|
1196
|
+
//
|
|
1197
|
+
// - If any host is connected:
|
|
1198
|
+
// - Give a turn to everyone else with tickets for that host
|
|
1199
|
+
// - Give a turn to one ticket for disconnected hosts, per 3s
|
|
1177
1200
|
function ReconnectRateLimiter(wait_time) {
|
|
1178
1201
|
var self = {}
|
|
1179
1202
|
|
|
1180
1203
|
self.conns = new Map() // Map<host, Set<url>>
|
|
1181
|
-
self.
|
|
1182
|
-
self.qs = [] // Array<{
|
|
1183
|
-
self.
|
|
1204
|
+
self.host_to_tickets = new Map() // Map<hostname, Array<ticket>>
|
|
1205
|
+
self.qs = [] // Array<{hostname, tickets: Array<ticket>, latest_turn}>
|
|
1206
|
+
self.latest_turn = 0
|
|
1184
1207
|
self.timer = null
|
|
1185
1208
|
|
|
1186
|
-
|
|
1209
|
+
// Resolves a batch of calls to `await get_turn(url)`.
|
|
1210
|
+
// - It may be just one
|
|
1211
|
+
// - Or it may be many
|
|
1212
|
+
// - Or it may be nothing
|
|
1213
|
+
// - If the wait_time has not expired
|
|
1214
|
+
// - Or if the queue of tickets is empty
|
|
1215
|
+
function give_turns () {
|
|
1187
1216
|
if (self.timer) clearTimeout(self.timer)
|
|
1188
1217
|
self.timer = null
|
|
1189
1218
|
|
|
1190
1219
|
if (!self.qs.length) return
|
|
1191
|
-
var
|
|
1192
|
-
var
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1220
|
+
var fully_disconnected = () => self.conns.size === 0
|
|
1221
|
+
var now = Date.now(),
|
|
1222
|
+
ticket_turn_time = () => wait_time + (fully_disconnected()
|
|
1223
|
+
? self.latest_turn
|
|
1224
|
+
: self.qs[0].latest_turn)
|
|
1225
|
+
|
|
1226
|
+
// Go through the queue of hosts and tickets, to pick some to resolve
|
|
1227
|
+
while (self.qs.length && now >= ticket_turn_time()) {
|
|
1228
|
+
// Remove the first host from top of the queue
|
|
1229
|
+
var host = self.qs.shift()
|
|
1230
|
+
|
|
1231
|
+
// Garbage collect hosts without tickets
|
|
1232
|
+
if (host.tickets.length === 0) {
|
|
1233
|
+
self.host_to_tickets.delete(host.hostname)
|
|
1198
1234
|
continue
|
|
1199
1235
|
}
|
|
1200
1236
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1237
|
+
// Give this ticket holder a turn!
|
|
1238
|
+
host.tickets.shift()()
|
|
1239
|
+
|
|
1240
|
+
// And reinsert the host back to the end
|
|
1241
|
+
self.qs.push(host)
|
|
1242
|
+
host.latest_turn = self.latest_turn = now
|
|
1204
1243
|
}
|
|
1205
1244
|
|
|
1245
|
+
// Set timer for the next round of get_turns() to run
|
|
1206
1246
|
if (self.qs.length)
|
|
1207
|
-
self.timer = setTimeout(
|
|
1208
|
-
|
|
1247
|
+
self.timer = setTimeout(give_turns,
|
|
1248
|
+
Math.max(0, ticket_turn_time() - now))
|
|
1209
1249
|
}
|
|
1210
1250
|
|
|
1211
1251
|
self.get_turn = async (url) => {
|
|
1212
|
-
var
|
|
1252
|
+
var hostname = new URL(url).host
|
|
1213
1253
|
|
|
1214
1254
|
// If host has connections, give turn immediately
|
|
1215
|
-
if (self.conns.has(
|
|
1255
|
+
if (self.conns.has(hostname)) return
|
|
1216
1256
|
|
|
1217
|
-
// console.log(`throttling reconn to ${url} (no conns yet to ${self.conns.size ?
|
|
1257
|
+
// console.log(`throttling reconn to ${url} (no conns yet to ${self.conns.size ? hostname : 'anything'})`)
|
|
1218
1258
|
|
|
1219
|
-
if (!self.
|
|
1220
|
-
var
|
|
1221
|
-
self.
|
|
1222
|
-
self.qs.unshift({
|
|
1259
|
+
if (!self.host_to_tickets.has(hostname)) {
|
|
1260
|
+
var tickets = []
|
|
1261
|
+
self.host_to_tickets.set(hostname, tickets)
|
|
1262
|
+
self.qs.unshift({hostname, tickets, latest_turn: 0})
|
|
1223
1263
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1264
|
+
|
|
1265
|
+
var p = new Promise(resolve =>
|
|
1266
|
+
// This resolve function becomes a ticket!
|
|
1267
|
+
self.host_to_tickets.get(hostname).push(resolve))
|
|
1268
|
+
give_turns()
|
|
1226
1269
|
await p
|
|
1227
1270
|
}
|
|
1228
1271
|
|
|
1272
|
+
// After you get a turn, and connect, it's your responsibility to call
|
|
1273
|
+
// this function to tell the rate limiter that you're online.
|
|
1229
1274
|
self.on_conn = url => {
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1275
|
+
// When a host connects, it's time to give a turn to everyone else
|
|
1276
|
+
// with tickets for that host
|
|
1277
|
+
|
|
1278
|
+
var hostname = new URL(url).host
|
|
1279
|
+
|
|
1280
|
+
// We keep track of the connections to each host so that we know which
|
|
1281
|
+
// ones are online and offline.
|
|
1282
|
+
if (!self.conns.has(hostname))
|
|
1283
|
+
self.conns.set(hostname, new Set()) // Initialize the set
|
|
1284
|
+
|
|
1285
|
+
// Add this connection to our memory!
|
|
1286
|
+
self.conns.get(hostname).add(url)
|
|
1234
1287
|
|
|
1235
|
-
// If there are
|
|
1236
|
-
var
|
|
1237
|
-
if (
|
|
1238
|
-
for (var
|
|
1239
|
-
|
|
1288
|
+
// If there are tickets waiting for this host, resolve them all immediately
|
|
1289
|
+
var tickets = self.host_to_tickets.get(hostname)
|
|
1290
|
+
if (tickets) {
|
|
1291
|
+
for (var ticket of tickets) ticket()
|
|
1292
|
+
tickets.splice(0, tickets.length)
|
|
1240
1293
|
}
|
|
1241
1294
|
|
|
1242
|
-
|
|
1295
|
+
give_turns()
|
|
1243
1296
|
}
|
|
1244
1297
|
|
|
1298
|
+
// After you get a turn, and disconnect, it's your responsibility to call
|
|
1299
|
+
// this function to tell the rate limiter that you're offline.
|
|
1245
1300
|
self.on_diss = url => {
|
|
1246
|
-
var
|
|
1247
|
-
var urls = self.conns.get(
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1301
|
+
var hostname = new URL(url).host
|
|
1302
|
+
var urls = self.conns.get(hostname)
|
|
1303
|
+
|
|
1304
|
+
console.assert(urls, 'ReconnectRateLimiter: out of sync! '
|
|
1305
|
+
+ 'Attempt to disconnect from url that is not known to be connected.')
|
|
1306
|
+
|
|
1307
|
+
// Remove this dropped connection from our set!
|
|
1308
|
+
urls.delete(url)
|
|
1309
|
+
|
|
1310
|
+
// And garbage collect the host's connections set if now empty
|
|
1311
|
+
if (urls.size === 0) self.conns.delete(hostname)
|
|
1252
1312
|
}
|
|
1253
1313
|
|
|
1254
1314
|
return self
|
package/index.sh
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
# Function to calculate Base64-encoded SHA256 hash
|
|
4
4
|
calculate_sha256() {
|
|
5
|
-
if command -v
|
|
6
|
-
|
|
5
|
+
if command -v openssl > /dev/null; then
|
|
6
|
+
openssl dgst -sha256 -binary | openssl base64
|
|
7
|
+
elif command -v shasum > /dev/null; then
|
|
8
|
+
shasum -a 256 | cut -d ' ' -f 1 | xxd -r -p | base64
|
|
7
9
|
elif command -v sha256sum > /dev/null; then
|
|
8
|
-
|
|
10
|
+
sha256sum | cut -d ' ' -f 1 | xxd -r -p | base64
|
|
9
11
|
else
|
|
10
|
-
echo "Error:
|
|
12
|
+
echo "Error: No SHA256 tool available." >&2
|
|
11
13
|
exit 1
|
|
12
14
|
fi
|
|
13
15
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "braidfs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.155",
|
|
4
4
|
"description": "braid technology synchronizing files and webpages",
|
|
5
5
|
"author": "Braid Working Group",
|
|
6
6
|
"repository": "braid-org/braidfs",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"braid-http": "~1.3.89",
|
|
10
10
|
"braid-text": "~0.5.20",
|
|
11
|
-
"braid-blob": "~0.0.
|
|
11
|
+
"braid-blob": "~0.0.84",
|
|
12
12
|
"chokidar": "^4.0.3",
|
|
13
13
|
"undici": "^7.18.2"
|
|
14
14
|
},
|