pmtiles-swarm 0.69.0 → 0.70.0

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 CHANGED
@@ -7,6 +7,33 @@
7
7
  ### 🐞 Bug fixes
8
8
  - _...Add new stuff here..._
9
9
 
10
+ ## 0.70.0
11
+ ### ✨ Features and improvements
12
+ - **An export works on the disk the archive is going to.** It worked under the data directory and
13
+ moved the finished file afterwards, which is the wrong disk twice over. The bytes have to go
14
+ where there is room for them, and a 700 GiB archive is not something a data directory is sized
15
+ for - while the disk chosen to hold the finished archive is, by definition.
16
+
17
+ The second problem was the move. `publish` renames within a filesystem and copies across one, so
18
+ a bake that worked elsewhere ended with a full copy of the whole archive - and until that
19
+ finished, the buffered tile data and the finished file both sat on the data directory's disk.
20
+ Working at the destination makes the last step a rename and removes the second full-size write.
21
+
22
+ Nothing appears at the destination until the end either way: the archive is assembled in one
23
+ pass at finalize, so until then `<destination>/bakes/<stack>/` holds the buffered tiles and the
24
+ checkpoint and no `.pmtiles` at all.
25
+
26
+ ### 🐞 Bug fixes
27
+ - **A stack said "without sharp installed, its tiles answer 501" whether or not sharp was
28
+ installed.** It is a conditional the console can resolve and the reader cannot: nothing on the
29
+ page said which way it fell, so the line told somebody with a working stack that it might not
30
+ be, and somebody with a broken one that it might be fine.
31
+
32
+ `/api/stacks` has always reported the codec. Now the stack row uses it. With one installed there
33
+ is nothing to say, so nothing is said - and the "needs a codec" badge is gone too, because a
34
+ warning on something that works teaches people to ignore warnings. Without one it says the tiles
35
+ answer 501 *here*, names the field responsible, and says what to install.
36
+
10
37
  ## 0.69.0
11
38
  ### ✨ Features and improvements
12
39
  - **An export merges several tiles at once.** It did one at a time, start to finish, before
@@ -40,9 +67,6 @@
40
67
  while it ran was never looked at again, and the export completed as though nobody had asked it
41
68
  to stop. Checked around each batch as well now.
42
69
 
43
- ### 🐞 Bug fixes
44
- - _...Add new stuff here..._
45
-
46
70
  ## 0.68.3
47
71
  ### 🐞 Bug fixes
48
72
  - **Exporting a stack failed on its first merged tile.** "Cannot transfer object of unsupported
@@ -789,6 +789,20 @@ One bake per stack at a time. Two runs of one recipe write the same checkpoint
789
789
  files over each other, and the second would resume the first's work believing it
790
790
  were its own.
791
791
 
792
+ The work happens **on the filesystem the archive is going to** — under
793
+ `<destination>/bakes/<stack>/` — not under the data directory. The bytes have
794
+ to go where there is room for them, and a 700 GiB archive is not something a
795
+ data directory is sized for, while the disk chosen to hold the finished archive
796
+ is by definition. It also turns the last step of a long job from a copy of the
797
+ whole archive into a rename, and means the buffered tile data and the finished
798
+ archive never both have to fit on the data directory's disk.
799
+
800
+ Nothing appears at the destination itself until the end. The archive is
801
+ assembled in one pass at finalize, so until then the directory holds the
802
+ buffered tiles and the checkpoint and no `.pmtiles` at all. Choosing a different
803
+ location for a resumed bake looks for the checkpoint in the new place and finds
804
+ none, so it starts over.
805
+
792
806
  ### What exists now
793
807
 
794
808
  `src/pmtiles-write.js` writes archives, `src/pmtiles-scan.js` reads back what one
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmtiles-swarm",
3
- "version": "0.69.0",
3
+ "version": "0.70.0",
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/bake-jobs.js CHANGED
@@ -24,9 +24,36 @@ import { outputFormat } from './stack-tile.js';
24
24
  * first's work as if it were its own.
25
25
  */
26
26
 
27
- /** Where unfinished bakes live, under the data directory. */
27
+ /** What the working directory is called, wherever it sits. */
28
28
  const WORK_DIR = 'bakes';
29
29
 
30
+ /**
31
+ * Where a bake does its work.
32
+ *
33
+ * On the filesystem the archive is going to, not under the data directory.
34
+ * Two reasons, and the first is the one that decides it: the bytes have to go
35
+ * somewhere there is room for them, and a 700 GiB archive is not something a
36
+ * data directory is sized for -- while the disk chosen to hold the finished
37
+ * archive is, by definition.
38
+ *
39
+ * The second falls out of the first. `publish` renames the finished file into
40
+ * place where it can and copies it where it cannot, so working on the
41
+ * destination's filesystem turns the last step of a long job from a full copy
42
+ * of the whole archive into a rename. That also removes the second full-size
43
+ * write, since the buffered tile data and the finished archive no longer have
44
+ * to exist on the data directory's disk at the same time.
45
+ *
46
+ * The directory is removed when the bake finishes. A cancelled one keeps it,
47
+ * which is the point of it.
48
+ * @param {object} job - The job, carrying where it will publish to.
49
+ * @param {object} config - The node's configuration.
50
+ * @returns {string} - The working directory.
51
+ */
52
+ export function workDirFor(job, config = {}) {
53
+ const root = job.publishDir ?? config.savePath ?? config.dataDir ?? './data';
54
+ return path.join(root, WORK_DIR, job.stackId);
55
+ }
56
+
30
57
  /** A bake that has finished is kept this long, so the console can report it. */
31
58
  const KEEP_FINISHED_MS = 10 * 60 * 1000;
32
59
 
@@ -173,11 +200,7 @@ export class BakeManager {
173
200
  * @returns {Promise<void>} - Resolves when the archive is in the catalog.
174
201
  */
175
202
  async #run(job, resolved, codec, options) {
176
- const workDir = path.join(
177
- this.#config.dataDir ?? './data',
178
- WORK_DIR,
179
- job.stackId,
180
- );
203
+ const workDir = workDirFor(job, this.#config);
181
204
  const destination = path.join(workDir, job.name);
182
205
  const format = outputFormat(resolved);
183
206
 
@@ -5225,7 +5225,7 @@ Every piece is hashed against the ` +
5225
5225
  },
5226
5226
  {
5227
5227
  key: 'stacks.bakePauseMs',
5228
- label: 'Pause between tiles while exporting',
5228
+ label: 'Pause between batches while exporting',
5229
5229
  type: 'number',
5230
5230
  placeholder: '0',
5231
5231
  unit: 'ms',
@@ -7042,11 +7042,15 @@ Every piece is hashed against the ` +
7042
7042
  // The shapes this node has, so a source can be clipped to one by name
7043
7043
  // rather than by remembering a filename.
7044
7044
  let stackCutlines = [];
7045
+ // Which codec is installed, or null. A stack row needs it to say whether
7046
+ // its pixel work is a problem here or merely a fact about the recipe.
7047
+ let stackCodec = null;
7045
7048
 
7046
7049
  const loadStacks = async () => {
7047
7050
  const body = await api('/api/stacks');
7048
7051
  const { stacks, codec } = body;
7049
7052
  stackCutlines = body.cutlines ?? [];
7053
+ stackCodec = codec ?? null;
7050
7054
  // A category resolving to a new build changes this, which is the
7051
7055
  // reason to poll at all -- but most polls change nothing, and
7052
7056
  // repainting a list somebody is about to click is worse than not.
@@ -7108,8 +7112,12 @@ Every piece is hashed against the ` +
7108
7112
  if (unresolved.length) {
7109
7113
  badges.push('<span class="pill warn">sources missing</span>');
7110
7114
  }
7111
- if (stack.needsCodec) {
7112
- badges.push('<span class="pill warn">needs a codec</span>');
7115
+ // Only where it is actually a problem. With a codec installed the
7116
+ // recipe asking for pixel work is a fact about the recipe, not
7117
+ // something wrong with it, and a warning badge on a working stack
7118
+ // teaches people to ignore warning badges.
7119
+ if (stack.needsCodec && !stackCodec) {
7120
+ badges.push('<span class="pill warn">no codec for this</span>');
7113
7121
  }
7114
7122
  if (stack.pinned) badges.push('<span class="pill">pinned</span>');
7115
7123
 
@@ -7191,10 +7199,10 @@ Every piece is hashed against the ` +
7191
7199
  }
7192
7200
  ${bakeLine}
7193
7201
  ${
7194
- stack.needsCodec
7195
- ? `<p class="muted">Serving this means decoding pixels
7196
- (<code>${escapeHtml(stack.needsCodec)}</code>). Without
7197
- <code>sharp</code> installed, its tiles answer 501.</p>`
7202
+ stack.needsCodec && !stackCodec
7203
+ ? `<p class="bad">Its tiles answer 501 here: serving this means
7204
+ decoding pixels (<code>${escapeHtml(stack.needsCodec)}</code>)
7205
+ and this node has no codec. <code>npm install sharp</code>.</p>`
7198
7206
  : ''
7199
7207
  }
7200
7208
  <table class="tight">