@procedures/mongo 0.9.1 → 0.10.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 +31 -0
- package/README.md +49 -36
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/storage/document-storage.d.ts +2 -0
- package/dist/storage/document-storage.d.ts.map +1 -1
- package/dist/storage/document-storage.js +1 -1
- package/dist/storage/document-storage.js.map +1 -1
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/materializer.d.ts +354 -0
- package/dist/storage/materializer.d.ts.map +1 -0
- package/dist/storage/materializer.js +933 -0
- package/dist/storage/materializer.js.map +1 -0
- package/package.json +3 -3
- package/src/index.ts +3 -0
- package/src/storage/document-storage.ts +1 -1
- package/src/storage/index.ts +3 -0
- package/src/storage/materializer.ts +1044 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# @procedures/mongo
|
|
2
2
|
|
|
3
|
+
## 0.10.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [254ee7b]
|
|
8
|
+
- @procedures/client@0.10.0
|
|
9
|
+
- @procedures/collections@0.10.0
|
|
10
|
+
|
|
11
|
+
## 0.9.2
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- e192622: New `ViewMaterializer`: materialized views driven by change streams. A view is a document of a views collection, with a pipeline over source collections and a target collection, for one partition. The materializer computes each view again when its sources change, in the order of the dependencies. It keeps the status of each view (`ok`, `errored`, `cycle`).
|
|
16
|
+
|
|
17
|
+
- The rows of a view are a generation, and the status names the current one. A reader filters with the new `viewFilter(view)`, so it reads one whole generation.
|
|
18
|
+
- A run writes only the rows that changed, and removes the rows that the result does not have, in one transaction. A larger view (`maxTransactionRows`), or a failed transaction, writes a whole new generation in groups. The previous generation stays for `generationGrace`. A pass in groups holds a lease on its view (`passLease`), so two processes can keep the same views. The time options are durations of `@procedures/client`.
|
|
19
|
+
- The stored `_id` of a row carries its partition and its generation: `{ partition, generation, id }` by default (`partitionTargetId`), or the form of the option `targetId`. Two partitions with the same keys share a target.
|
|
20
|
+
- Targets from an older materializer, with the plain key as `_id`, need no migration step. The first pass of each view replaces the rows of its partition.
|
|
21
|
+
- Each pipeline goes through `scopePipeline` by default. It reads only its partition, in its sources, and the current generation of a target.
|
|
22
|
+
|
|
23
|
+
The code comes from the server of Graph (its split plan, part P4). The jobs of CI with a replica set start MongoDB with the test commands, for the failpoint tests.
|
|
24
|
+
- 054ba7d: `ViewMaterializer`: a change reaches only the views of its partition when the partition is known (roadmap CLI-37). The filter only removes work: a change whose partition is unknown reaches the views of each partition, as before.
|
|
25
|
+
|
|
26
|
+
- A delete has no document in its change event. Its partition comes from the pre-image, when its collection has `changeStreamPreAndPostImages`.
|
|
27
|
+
- The new option `storageSources` gives, for a collection of `MongoDocumentStorage`, the function from the storage partition (at the start of the id) to the partition of the views. `identityPartition` is for two partitions that are the same.
|
|
28
|
+
- With pre-images, an update that moves a document to another partition also reaches the views of its old partition.
|
|
29
|
+
- Without pre-images, a replace and an update of the partition field reach the views of each partition, as a delete does. The document can leave any partition.
|
|
30
|
+
- With `sourceFields`, the keys of an update are paths with dots: a `$push` (`tags.3`), a `$set` of `a.b`, a removal or a truncated array. Each key counts at the field, under it or above it, and the positions in arrays do not count (`lines.0.price` is `lines.price`). An update of the partition field starts a pass too.
|
|
31
|
+
- @procedures/client@0.9.2
|
|
32
|
+
- @procedures/collections@0.9.2
|
|
33
|
+
|
|
3
34
|
## 0.9.1
|
|
4
35
|
|
|
5
36
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -699,6 +699,49 @@ const { pipeline: bounded, limited } = boundPipeline(scoped, 5000);
|
|
|
699
699
|
- The function does not change the pipeline of the caller.
|
|
700
700
|
- `MongoDocumentStorage.aggregate(pipeline, { maxTime, maxResults })` is read-only, reads only its own collection, and keeps the tombstones out. It gives `{ docs, truncated }`. The defaults are `seconds(10)` and 5000 documents. `maxTime` is a duration of `@procedures/client`.
|
|
701
701
|
|
|
702
|
+
### Materialized views: `ViewMaterializer`
|
|
703
|
+
|
|
704
|
+
A view is a document of a views collection: a pipeline over source collections, whose result goes into a target collection, for one partition. `ViewMaterializer` keeps each target equal to the result of its pipeline, with change streams. Change streams need a replica set.
|
|
705
|
+
|
|
706
|
+
```ts
|
|
707
|
+
import { ViewMaterializer, viewFilter } from "@procedures/mongo";
|
|
708
|
+
|
|
709
|
+
await db.collection("_aggregations").insertOne({
|
|
710
|
+
_id: "totals",
|
|
711
|
+
_part: "tenant-1",
|
|
712
|
+
collections: ["orders"],
|
|
713
|
+
pipeline: [{ $group: { _id: "$status", count: { $sum: 1 } } }],
|
|
714
|
+
targetCollection: "orderTotals",
|
|
715
|
+
});
|
|
716
|
+
const views = new ViewMaterializer({ db: () => db });
|
|
717
|
+
await views.start();
|
|
718
|
+
|
|
719
|
+
// A reader of the target reads the current generation of the view
|
|
720
|
+
const view = await db.collection("_aggregations").findOne({ _id: "totals" });
|
|
721
|
+
const totals = await db.collection("orderTotals").find(viewFilter(view)).toArray();
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
- A change of a source starts a pass of its partition after the burst (`debounce`). A pass computes only the views whose sources changed, in the order of their dependencies. A view whose result changed makes its target dirty, so the views that read it are in the same pass.
|
|
725
|
+
- A run reads the result in groups of 1000. Each stored row keeps the hash of its content (`_viewHash`), so a row that did not change writes nothing.
|
|
726
|
+
- The rows of a view are a generation (`_viewGen`), and the status of the view names the current one. A reader of a target filters with `viewFilter(view)`, so it reads one whole generation. A plain query can see two generations.
|
|
727
|
+
- A run writes in one transaction: the changed rows of the current generation, and the removal of the rows that the result does not have. A reader does not see a part of a run.
|
|
728
|
+
- A view with more rows than `maxTransactionRows` (default 10000), or whose transaction fails, writes a whole new generation in groups. Then its status names the new generation. The previous one stays for `generationGrace` (default 60 s, on the time of the server), so a reader of it can finish. Then it goes.
|
|
729
|
+
- A pass in groups holds a lease on its view (`passLease`, default 60 s, on the time of the server), and renews it after each group. Another process leaves the view and the rows of the lease alone. The change of generation needs the lease and the generation of the start of the pass. A pass that loses either one gives way, and the view runs again later.
|
|
730
|
+
- A view in a cycle of views stays idle. Its status is `cycle` until the cycle ends.
|
|
731
|
+
- The status of each view (`ok`, `errored` with `lastError`, `cycle`) goes into the field `_materializerStatus` of its document.
|
|
732
|
+
- Each pipeline goes through `scopePipeline` by default. It reads only the documents of the partition, in its sources, and only the current generation of the target of another view. It cannot have `$merge` or `$out`. The option `scope` gives another guard, with the same filter.
|
|
733
|
+
- A stream that ends opens again after `rewatch`, and a pass catches up. The times of the options are durations (`ms(100)`, `seconds(60)`) of `@procedures/client`.
|
|
734
|
+
- A delete has no document in its change event. It reaches only the views of its partition when its pre-image tells the partition (a collection with `changeStreamPreAndPostImages`). Without a pre-image, a delete, a replace and an update of the partition field reach the views of each partition.
|
|
735
|
+
- In a source of `storageSources`, the id of a `MongoDocumentStorage` document tells the storage partition of such a change. The function of that source gives the partition of the views (`identityPartition` when the two are the same). Else the delete reaches the views of each partition.
|
|
736
|
+
- With pre-images, an update that moves a document to another partition also reaches the views of its old partition.
|
|
737
|
+
- `sourceFields` gives the fields of each source, as paths with dots. An update starts a pass when it changes one of them (at the field, under it or above it), or the partition field. The positions in arrays do not count: `lines.0.price` is `lines.price`. The partition field is a top-level field.
|
|
738
|
+
- The stop has the parts of a transport of a service: `stopIntake()`, `drain(deadline, signal)`, `inFlight()` and `close()`. `stop()` does the four. A host composes them with no adapter.
|
|
739
|
+
- The option `clock` gives the clock of the timers, for example a fake clock in a test. The timers do not keep a process alive.
|
|
740
|
+
- The target is a plain collection that only the materializer writes. Two views of one partition do not share a target. The stored `_id` of a row carries its partition and its generation: by default `{ partition, generation, id }` (`partitionTargetId`), where `id` is the `_id` of the row. Thus two partitions with the same keys share a target.
|
|
741
|
+
- The option `targetId` gives another form of the stored `_id`. Two partitions, or two generations, must not give the same `_id`. The write of a row checks the partition, so the view of the second partition fails, and the row of the first one stays. A form without the generation works only in one transaction.
|
|
742
|
+
- A pass keeps in memory the key of each row of the result (at most `maxRows`) and one group of rows. It keeps nothing between passes.
|
|
743
|
+
- An app with targets from an older materializer (the plain key as `_id`, no generation) needs no migration step. The first pass of each view writes its rows in a generation, and removes the rows of its partition with no generation. A row without the partition field stays.
|
|
744
|
+
|
|
702
745
|
## Integration with Ecosystem
|
|
703
746
|
|
|
704
747
|
### Bundle Packages
|
|
@@ -737,42 +780,12 @@ const call = createTypedCaller<AllProcedures>(client.call);
|
|
|
737
780
|
|
|
738
781
|
## Package Structure
|
|
739
782
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
│ ├── register.ts # Auto-registration
|
|
747
|
-
│ ├── index.ts # Main entry point
|
|
748
|
-
│ ├── procedures/
|
|
749
|
-
│ │ ├── schema.ts # Minimal schema helpers
|
|
750
|
-
│ │ ├── index.ts # Procedure barrel export
|
|
751
|
-
│ │ ├── database.ping.ts
|
|
752
|
-
│ │ ├── database.info.ts
|
|
753
|
-
│ │ ├── collections.list.ts
|
|
754
|
-
│ │ ├── collections.create.ts
|
|
755
|
-
│ │ ├── collections.drop.ts
|
|
756
|
-
│ │ ├── collections.stats.ts
|
|
757
|
-
│ │ ├── documents.find.ts
|
|
758
|
-
│ │ ├── documents.get.ts
|
|
759
|
-
│ │ ├── documents.insert.ts
|
|
760
|
-
│ │ ├── documents.update.ts
|
|
761
|
-
│ │ ├── documents.delete.ts
|
|
762
|
-
│ │ ├── documents.count.ts
|
|
763
|
-
│ │ ├── documents.aggregate.ts
|
|
764
|
-
│ │ ├── documents.distinct.ts
|
|
765
|
-
│ │ ├── documents.bulk.ts
|
|
766
|
-
│ │ ├── documents.explain.ts
|
|
767
|
-
│ │ ├── indexes.list.ts
|
|
768
|
-
│ │ ├── indexes.create.ts
|
|
769
|
-
│ │ ├── indexes.drop.ts
|
|
770
|
-
│ │ └── watch.ts # mongo.watch
|
|
771
|
-
├── dist/ # Compiled output
|
|
772
|
-
├── package.json
|
|
773
|
-
├── tsconfig.json
|
|
774
|
-
└── README.md
|
|
775
|
-
```
|
|
783
|
+
- `src/index.ts`: the entry. It registers nothing.
|
|
784
|
+
- `src/module.ts`: `mongoModule`, the procedures as data. `src/register.ts` registers them in the global registry.
|
|
785
|
+
- `src/connection.ts`: the connection and its management.
|
|
786
|
+
- `src/scope.ts`: the database and the collection of a call.
|
|
787
|
+
- `src/procedures/`: one file for each procedure, with the schemas of the inputs.
|
|
788
|
+
- `src/storage/`: `MongoStorage`, `MongoDocumentStorage`, `scopePipeline` and `ViewMaterializer`.
|
|
776
789
|
|
|
777
790
|
## Development
|
|
778
791
|
|