@gmod/tabix 3.5.5 → 3.6.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/README.md CHANGED
@@ -102,18 +102,19 @@ opening the file directly.
102
102
 
103
103
  ### `new TabixIndexedFile(args)`
104
104
 
105
- | Arg | Type | Description |
106
- | ---------------- | -------------------- | ------------------------------------------------------------------------------------------- |
107
- | `path` | `string?` | Local file path |
108
- | `url` | `string?` | Remote URL |
109
- | `filehandle` | `GenericFilehandle?` | Custom filehandle (from [generic-filehandle2](https://github.com/GMOD/generic-filehandle2)) |
110
- | `tbiPath` | `string?` | TBI index path (defaults to `path + '.tbi'`) |
111
- | `tbiUrl` | `string?` | TBI index URL |
112
- | `tbiFilehandle` | `GenericFilehandle?` | TBI index filehandle |
113
- | `csiPath` | `string?` | CSI index path |
114
- | `csiUrl` | `string?` | CSI index URL |
115
- | `csiFilehandle` | `GenericFilehandle?` | CSI index filehandle |
116
- | `chunkCacheSize` | `number?` | Chunk LRU cache budget, in _decompressed_ bytes (default 100 MiB) |
105
+ | Arg | Type | Description |
106
+ | ------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
107
+ | `path` | `string?` | Local file path |
108
+ | `url` | `string?` | Remote URL |
109
+ | `filehandle` | `GenericFilehandle?` | Custom filehandle (from [generic-filehandle2](https://github.com/GMOD/generic-filehandle2)) |
110
+ | `tbiPath` | `string?` | TBI index path (defaults to `path + '.tbi'`) |
111
+ | `tbiUrl` | `string?` | TBI index URL |
112
+ | `tbiFilehandle` | `GenericFilehandle?` | TBI index filehandle |
113
+ | `csiPath` | `string?` | CSI index path |
114
+ | `csiUrl` | `string?` | CSI index URL |
115
+ | `csiFilehandle` | `GenericFilehandle?` | CSI index filehandle |
116
+ | `chunkCacheSize` | `number?` | Chunk LRU cache budget, in _decompressed_ bytes (default 1 GiB). A retention bound, not a bound on peak memory. Size it to hold several queries: below one query's working set the hit rate drops to zero while the memory is retained anyway |
117
+ | `chunkCacheIdleTimeoutMs` | `number?` | Drop a cached chunk once nothing has read it for this long (default 3 minutes, `0` disables). The only thing that lowers the cache while nothing is happening, and what makes the budget above a peak rather than a resting level |
117
118
 
118
119
  ### `getLines(refName, start, end, opts)`
119
120
 
@@ -50,6 +50,13 @@ export interface IndexData {
50
50
  export default abstract class IndexFile {
51
51
  filehandle: GenericFilehandle;
52
52
  private parseP?;
53
+ /**
54
+ * The signal `parseP` was started under, while it is still in flight. The
55
+ * index is parsed once and shared by every query against the file, so without
56
+ * this the first query to arrive would own a read all the others depend on —
57
+ * see {@link parse}.
58
+ */
59
+ private parseSignal?;
53
60
  constructor({ filehandle }: {
54
61
  filehandle: GenericFilehandle;
55
62
  });
@@ -111,8 +118,34 @@ export default abstract class IndexFile {
111
118
  * @internal
112
119
  */
113
120
  blocksForRange(refName: string, min: number, max: number, opts?: Options): Promise<Chunk[]>;
114
- /** @internal */
115
- parse(opts?: Options): Promise<IndexData>;
121
+ /**
122
+ * Parse the index, or join the parse already running.
123
+ *
124
+ * The index is downloaded and parsed once for the life of this object, so it
125
+ * is the one read here that is shared between queries — and therefore the one
126
+ * place a cancellation can leak from the query that asked for it to a query
127
+ * that did not. `_parse` hands `opts` straight to `readIndexBytes`, so
128
+ * without this the first query to arrive owns a read every other query
129
+ * depends on: when it pans away, every concurrent query fails with its abort.
130
+ *
131
+ * A caller that joined someone else's parse and saw it fail because *they*
132
+ * aborted starts over rather than inheriting the failure — once, then
133
+ * propagates. Bounding it at one attempt is what jbrowse's
134
+ * `RemoteFileWithRangeCache.joinChunk` does with the same retry one layer
135
+ * down, and for the reason it gives: the pathological case becomes one
136
+ * duplicate parse rather than a recursion whose depth depends on how the
137
+ * aborts interleave.
138
+ *
139
+ * A retry rather than the reference count `ChunkCache` uses, because the
140
+ * index is parsed once for the life of the object: there is no repeated waste
141
+ * to recover, and this is a dozen lines against restructuring the memo.
142
+ * `@gmod/bam`'s `IndexFile` and `@gmod/cram`'s `CraiIndex` make the same
143
+ * split for the same reason.
144
+ *
145
+ * @internal
146
+ */
147
+ parse(opts?: Options, retried?: boolean): Promise<IndexData>;
148
+ private startParse;
116
149
  /** @internal */
117
150
  hasRefSeq(seqId: number, opts?: Options): Promise<boolean>;
118
151
  }
package/dist/indexFile.js CHANGED
@@ -5,6 +5,13 @@ const util_ts_1 = require("./util.js");
5
5
  class IndexFile {
6
6
  filehandle;
7
7
  parseP;
8
+ /**
9
+ * The signal `parseP` was started under, while it is still in flight. The
10
+ * index is parsed once and shared by every query against the file, so without
11
+ * this the first query to arrive would own a read all the others depend on —
12
+ * see {@link parse}.
13
+ */
14
+ parseSignal;
8
15
  constructor({ filehandle }) {
9
16
  this.filehandle = filehandle;
10
17
  }
@@ -68,13 +75,71 @@ class IndexFile {
68
75
  }
69
76
  return (0, util_ts_1.optimizeChunks)(chunks, this.lowestOffset(ba, min, indexData));
70
77
  }
71
- /** @internal */
72
- async parse(opts = {}) {
73
- this.parseP ??= this._parse(opts).catch((error) => {
74
- this.parseP = undefined;
75
- throw error;
78
+ // SYNC: ~/src/gmod/bam-js/src/indexFile.ts parse — same owner-signal
79
+ // tracking and one-attempt retry, and the same reasoning below.
80
+ /**
81
+ * Parse the index, or join the parse already running.
82
+ *
83
+ * The index is downloaded and parsed once for the life of this object, so it
84
+ * is the one read here that is shared between queries — and therefore the one
85
+ * place a cancellation can leak from the query that asked for it to a query
86
+ * that did not. `_parse` hands `opts` straight to `readIndexBytes`, so
87
+ * without this the first query to arrive owns a read every other query
88
+ * depends on: when it pans away, every concurrent query fails with its abort.
89
+ *
90
+ * A caller that joined someone else's parse and saw it fail because *they*
91
+ * aborted starts over rather than inheriting the failure — once, then
92
+ * propagates. Bounding it at one attempt is what jbrowse's
93
+ * `RemoteFileWithRangeCache.joinChunk` does with the same retry one layer
94
+ * down, and for the reason it gives: the pathological case becomes one
95
+ * duplicate parse rather than a recursion whose depth depends on how the
96
+ * aborts interleave.
97
+ *
98
+ * A retry rather than the reference count `ChunkCache` uses, because the
99
+ * index is parsed once for the life of the object: there is no repeated waste
100
+ * to recover, and this is a dozen lines against restructuring the memo.
101
+ * `@gmod/bam`'s `IndexFile` and `@gmod/cram`'s `CraiIndex` make the same
102
+ * split for the same reason.
103
+ *
104
+ * @internal
105
+ */
106
+ async parse(opts = {}, retried = false) {
107
+ (0, util_ts_1.throwIfAborted)(opts.signal);
108
+ const pending = this.parseP;
109
+ if (!pending) {
110
+ return this.startParse(opts);
111
+ }
112
+ // read before awaiting: the owner is forgotten as soon as the parse settles
113
+ const ownerSignal = this.parseSignal;
114
+ try {
115
+ return await pending;
116
+ }
117
+ catch (e) {
118
+ if (retried || !ownerSignal?.aborted || opts.signal?.aborted) {
119
+ throw e;
120
+ }
121
+ return this.parse(opts, true);
122
+ }
123
+ }
124
+ startParse(opts) {
125
+ const pending = this._parse(opts);
126
+ this.parseP = pending;
127
+ this.parseSignal = opts.signal;
128
+ // Drop a rejection rather than keeping it, so one transient failure does not
129
+ // poison the index for the lifetime of the file. Both branches are
130
+ // identity-checked so a retry started after this settles is not cleared by
131
+ // the attempt it already replaced.
132
+ pending.then(() => {
133
+ if (this.parseP === pending) {
134
+ this.parseSignal = undefined;
135
+ }
136
+ }, () => {
137
+ if (this.parseP === pending) {
138
+ this.parseP = undefined;
139
+ this.parseSignal = undefined;
140
+ }
76
141
  });
77
- return this.parseP;
142
+ return pending;
78
143
  }
79
144
  /** @internal */
80
145
  async hasRefSeq(seqId, opts = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"indexFile.js","sourceRoot":"","sources":["../src/indexFile.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"indexFile.js","sourceRoot":"","sources":["../src/indexFile.ts"],"names":[],"mappings":"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"}