pmtiles-swarm 0.5.4 โ 0.5.6
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/CHANGELOG.md +26 -0
- package/package.json +1 -1
- package/src/prewarm.js +23 -2
- package/src/web/index.html +39 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,32 @@
|
|
|
7
7
|
### ๐ Bug fixes
|
|
8
8
|
- _...Add new stuff here..._
|
|
9
9
|
|
|
10
|
+
## 0.5.6
|
|
11
|
+
### ๐ Bug fixes
|
|
12
|
+
- **The Pieces and Peers tabs redraw themselves.** A detail pane is filled once, the first time
|
|
13
|
+
it is opened โ right for the panes describing an archive, whose name and source and trackers
|
|
14
|
+
do not change, and wrong for the two describing what is happening *now*. Pieces arrived and
|
|
15
|
+
peers came and went behind a picture taken when the tab was opened, and the only way to see
|
|
16
|
+
the current one was to close the panel and open it again, or reload the page. Those two now
|
|
17
|
+
follow the same three-second tick as the archive table. The rest stay lazy, since redrawing a
|
|
18
|
+
pane nobody is watching is requests every tick for nothing โ which is why they load on demand
|
|
19
|
+
in the first place. Only while visible, and never queued behind a redraw still in flight: a
|
|
20
|
+
piece map of a large archive is a real request, and three seconds is not long enough to assume
|
|
21
|
+
the last one finished.
|
|
22
|
+
|
|
23
|
+
## 0.5.5
|
|
24
|
+
### ๐ Bug fixes
|
|
25
|
+
- **An archive could retire itself from head-warming and never say so.** The test for "already
|
|
26
|
+
read" was `summary.format !== 'pbf'`, and any stored summary *without* a format satisfies
|
|
27
|
+
that โ so it counted as read, not vector, and therefore finished. A read that raced its
|
|
28
|
+
deadline leaves exactly such a summary behind, and from then on the archive was permanently
|
|
29
|
+
ineligible: no attempts, no log lines, and nothing to explain the silence. A summary now only
|
|
30
|
+
counts as an answer if it actually names a format.
|
|
31
|
+
- **A read that never settles no longer disables warming for good.** The flag that stops two
|
|
32
|
+
reads running at once was held for the life of the process by a read that never returned, and
|
|
33
|
+
every later pass then returned at its first line โ silently, for every archive. It is
|
|
34
|
+
abandoned after three times the metadata timeout, with a line saying so.
|
|
35
|
+
|
|
10
36
|
## 0.5.4
|
|
11
37
|
### โจ Features and improvements
|
|
12
38
|
- **The head-warm waits sensibly at both ends.** It used to read at the instant the node
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmtiles-swarm",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "BitTorrent distribution for PMTiles map archives: create torrents, watch folders, publish and subscribe to RSS feeds, and seed through qBittorrent or an embedded client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/prewarm.js
CHANGED
|
@@ -58,6 +58,7 @@ export class HeadWarmer {
|
|
|
58
58
|
#attempts = new Map();
|
|
59
59
|
#waiting = new Set();
|
|
60
60
|
#running = false;
|
|
61
|
+
#runningSince = 0;
|
|
61
62
|
|
|
62
63
|
/**
|
|
63
64
|
* @param {object} tiles - The tile store, for `summarize`.
|
|
@@ -94,8 +95,13 @@ export class HeadWarmer {
|
|
|
94
95
|
// archive this can say anything about.
|
|
95
96
|
if (entry.kind && entry.kind !== 'pmtiles') return false;
|
|
96
97
|
|
|
98
|
+
// A summary that names a format is one a header was actually read for.
|
|
99
|
+
// Anything else โ an empty object, or one left behind by a read that raced
|
|
100
|
+
// its deadline โ is not an answer, and treating it as one retired the
|
|
101
|
+
// archive permanently: no logs, no retries, nothing to explain the silence.
|
|
97
102
|
const summary = entry.pmtiles;
|
|
98
|
-
|
|
103
|
+
const read = Boolean(summary?.format);
|
|
104
|
+
if (read && (summary.format !== 'pbf' || summary.vectorLayers)) {
|
|
99
105
|
return false;
|
|
100
106
|
}
|
|
101
107
|
|
|
@@ -137,12 +143,27 @@ export class HeadWarmer {
|
|
|
137
143
|
* @returns {Promise<object|null>} - The entry warmed, or null.
|
|
138
144
|
*/
|
|
139
145
|
async sweep() {
|
|
140
|
-
if (!this.enabled
|
|
146
|
+
if (!this.enabled) return null;
|
|
147
|
+
|
|
148
|
+
if (this.#running) {
|
|
149
|
+
// A read that never settles would otherwise hold this flag for the life
|
|
150
|
+
// of the process and stop every archive from ever being warmed again โ
|
|
151
|
+
// silently, since nothing logs a pass that returns at the first line.
|
|
152
|
+
const stuck = (this.#config.tiles?.metadataTimeoutMs ?? 120000) * 3;
|
|
153
|
+
if (this.#now() - this.#runningSince < stuck) return null;
|
|
154
|
+
console.warn(
|
|
155
|
+
`[warm] a read has been running for ${Math.round(
|
|
156
|
+
(this.#now() - this.#runningSince) / 1000,
|
|
157
|
+
)}s and is being abandoned`,
|
|
158
|
+
);
|
|
159
|
+
this.#running = false;
|
|
160
|
+
}
|
|
141
161
|
|
|
142
162
|
const entry = this.#catalog.list().find((candidate) => this.due(candidate));
|
|
143
163
|
if (!entry) return null;
|
|
144
164
|
|
|
145
165
|
this.#running = true;
|
|
166
|
+
this.#runningSince = this.#now();
|
|
146
167
|
try {
|
|
147
168
|
// The long timeout, not the interactive one: this is a byte range from
|
|
148
169
|
// an archive nobody has asked for a piece of yet.
|
package/src/web/index.html
CHANGED
|
@@ -729,6 +729,21 @@
|
|
|
729
729
|
// before the node has said whether its engine applies one.
|
|
730
730
|
let incompleteMarker = null;
|
|
731
731
|
let activeTab = 'general';
|
|
732
|
+
/**
|
|
733
|
+
* Panes worth redrawing while they are open.
|
|
734
|
+
*
|
|
735
|
+
* A detail pane is filled once, the first time it is shown, which is
|
|
736
|
+
* right for the ones describing an archive: its name, its source, its
|
|
737
|
+
* trackers. It is wrong for these two, which describe what is happening
|
|
738
|
+
* *now* โ pieces arriving and peers coming and going โ and which sat
|
|
739
|
+
* frozen until the reader thought to close the panel and open it again.
|
|
740
|
+
*
|
|
741
|
+
* The rest stay as they are. Refreshing a pane nobody is watching is
|
|
742
|
+
* four requests a tick for nothing, which is why they are loaded lazily
|
|
743
|
+
* in the first place.
|
|
744
|
+
*/
|
|
745
|
+
const LIVE_PANES = new Set(['pieces', 'peers']);
|
|
746
|
+
let refreshingPane = false;
|
|
732
747
|
|
|
733
748
|
// What the header switch shows, so a click knows what it is toggling to.
|
|
734
749
|
let speedState = null;
|
|
@@ -894,6 +909,7 @@
|
|
|
894
909
|
` ยท <b>${status.archives}</b> archives`;
|
|
895
910
|
archives = list;
|
|
896
911
|
renderRows();
|
|
912
|
+
refreshOpenPane();
|
|
897
913
|
} catch (error) {
|
|
898
914
|
$('status').textContent = `disconnected: ${error.message}`;
|
|
899
915
|
}
|
|
@@ -1072,6 +1088,29 @@
|
|
|
1072
1088
|
({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c],
|
|
1073
1089
|
);
|
|
1074
1090
|
|
|
1091
|
+
/**
|
|
1092
|
+
* Redraws the open detail pane, when it is one that changes by itself.
|
|
1093
|
+
*
|
|
1094
|
+
* Guarded against overlapping, because a piece map of a large archive is
|
|
1095
|
+
* a real request and a slow one must not have a queue of its successors
|
|
1096
|
+
* behind it.
|
|
1097
|
+
* @returns {void}
|
|
1098
|
+
*/
|
|
1099
|
+
function refreshOpenPane() {
|
|
1100
|
+
if (refreshingPane || !selected || !LIVE_PANES.has(activeTab)) return;
|
|
1101
|
+
const pane = document.querySelector(`[data-pane="${activeTab}"]`);
|
|
1102
|
+
// Only while it is on screen: a hidden pane is one the reader moved
|
|
1103
|
+
// away from, and the panel may have been replaced since.
|
|
1104
|
+
if (!pane || pane.hidden) return;
|
|
1105
|
+
|
|
1106
|
+
refreshingPane = true;
|
|
1107
|
+
Promise.resolve(fillPane(activeTab, selected))
|
|
1108
|
+
.catch(() => {})
|
|
1109
|
+
.finally(() => {
|
|
1110
|
+
refreshingPane = false;
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1075
1114
|
/**
|
|
1076
1115
|
* Fills one detail tab, the first time it is opened.
|
|
1077
1116
|
* @param {string} name - Which pane.
|