@streamr/node 100.0.0-rc.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/LICENSE +78 -0
- package/README.md +54 -0
- package/bin/broker.ts +36 -0
- package/bin/config-wizard.js +4 -0
- package/bin/config-wizard.ts +17 -0
- package/bin/delete-expired-data.ts +41 -0
- package/bin/entry-point.ts +27 -0
- package/configs/development-1.env.json +68 -0
- package/configs/development-2.env.json +53 -0
- package/configs/development-3.env.json +53 -0
- package/configs/development-prod-resend.env.json +21 -0
- package/configs/docker-1.env.json +72 -0
- package/configs/docker-2.env.json +58 -0
- package/configs/docker-3.env.json +58 -0
- package/configuration.md +38 -0
- package/dist/bin/broker.d.ts +2 -0
- package/dist/bin/broker.js +42 -0
- package/dist/bin/broker.js.map +1 -0
- package/dist/bin/config-wizard.d.ts +2 -0
- package/dist/bin/config-wizard.js +22 -0
- package/dist/bin/config-wizard.js.map +1 -0
- package/dist/bin/delete-expired-data.d.ts +2 -0
- package/dist/bin/delete-expired-data.js +44 -0
- package/dist/bin/delete-expired-data.js.map +1 -0
- package/dist/bin/entry-point.d.ts +2 -0
- package/dist/bin/entry-point.js +28 -0
- package/dist/bin/entry-point.js.map +1 -0
- package/dist/package.json +78 -0
- package/dist/src/Plugin.d.ts +33 -0
- package/dist/src/Plugin.js +43 -0
- package/dist/src/Plugin.js.map +1 -0
- package/dist/src/apiAuthentication.d.ts +4 -0
- package/dist/src/apiAuthentication.js +16 -0
- package/dist/src/apiAuthentication.js.map +1 -0
- package/dist/src/broker.d.ts +8 -0
- package/dist/src/broker.js +69 -0
- package/dist/src/broker.js.map +1 -0
- package/dist/src/config/ConfigWizard.d.ts +5 -0
- package/dist/src/config/ConfigWizard.js +466 -0
- package/dist/src/config/ConfigWizard.js.map +1 -0
- package/dist/src/config/config.d.ts +26 -0
- package/dist/src/config/config.js +92 -0
- package/dist/src/config/config.js.map +1 -0
- package/dist/src/config/config.schema.json +86 -0
- package/dist/src/config/definitions.schema.json +35 -0
- package/dist/src/config/migration.d.ts +6 -0
- package/dist/src/config/migration.js +210 -0
- package/dist/src/config/migration.js.map +1 -0
- package/dist/src/config/validateConfig.d.ts +4 -0
- package/dist/src/config/validateConfig.js +40 -0
- package/dist/src/config/validateConfig.js.map +1 -0
- package/dist/src/exports.d.ts +3 -0
- package/dist/src/exports.js +6 -0
- package/dist/src/exports.js.map +1 -0
- package/dist/src/helpers/PayloadFormat.d.ts +19 -0
- package/dist/src/helpers/PayloadFormat.js +85 -0
- package/dist/src/helpers/PayloadFormat.js.map +1 -0
- package/dist/src/helpers/applyPluginClientConfigs.d.ts +3 -0
- package/dist/src/helpers/applyPluginClientConfigs.js +29 -0
- package/dist/src/helpers/applyPluginClientConfigs.js.map +1 -0
- package/dist/src/helpers/fetchOrThrow.d.ts +2 -0
- package/dist/src/helpers/fetchOrThrow.js +20 -0
- package/dist/src/helpers/fetchOrThrow.js.map +1 -0
- package/dist/src/helpers/generateMnemonicFromAddress.d.ts +5 -0
- package/dist/src/helpers/generateMnemonicFromAddress.js +16 -0
- package/dist/src/helpers/generateMnemonicFromAddress.js.map +1 -0
- package/dist/src/helpers/multiply.d.ts +1 -0
- package/dist/src/helpers/multiply.js +10 -0
- package/dist/src/helpers/multiply.js.map +1 -0
- package/dist/src/helpers/parser.d.ts +9 -0
- package/dist/src/helpers/parser.js +62 -0
- package/dist/src/helpers/parser.js.map +1 -0
- package/dist/src/helpers/partitions.d.ts +8 -0
- package/dist/src/helpers/partitions.js +32 -0
- package/dist/src/helpers/partitions.js.map +1 -0
- package/dist/src/helpers/weightedSample.d.ts +16 -0
- package/dist/src/helpers/weightedSample.js +35 -0
- package/dist/src/helpers/weightedSample.js.map +1 -0
- package/dist/src/httpServer.d.ts +16 -0
- package/dist/src/httpServer.js +71 -0
- package/dist/src/httpServer.js.map +1 -0
- package/dist/src/pluginRegistry.d.ts +3 -0
- package/dist/src/pluginRegistry.js +35 -0
- package/dist/src/pluginRegistry.js.map +1 -0
- package/dist/src/plugins/consoleMetrics/ConsoleMetricsPlugin.d.ts +12 -0
- package/dist/src/plugins/consoleMetrics/ConsoleMetricsPlugin.js +36 -0
- package/dist/src/plugins/consoleMetrics/ConsoleMetricsPlugin.js.map +1 -0
- package/dist/src/plugins/consoleMetrics/config.schema.json +18 -0
- package/dist/src/plugins/http/HttpPlugin.d.ts +8 -0
- package/dist/src/plugins/http/HttpPlugin.js +23 -0
- package/dist/src/plugins/http/HttpPlugin.js.map +1 -0
- package/dist/src/plugins/http/config.schema.json +12 -0
- package/dist/src/plugins/http/publishEndpoint.d.ts +3 -0
- package/dist/src/plugins/http/publishEndpoint.js +72 -0
- package/dist/src/plugins/http/publishEndpoint.js.map +1 -0
- package/dist/src/plugins/info/InfoPlugin.d.ts +9 -0
- package/dist/src/plugins/info/InfoPlugin.js +31 -0
- package/dist/src/plugins/info/InfoPlugin.js.map +1 -0
- package/dist/src/plugins/info/config.schema.json +12 -0
- package/dist/src/plugins/mqtt/Bridge.d.ts +40 -0
- package/dist/src/plugins/mqtt/Bridge.js +136 -0
- package/dist/src/plugins/mqtt/Bridge.js.map +1 -0
- package/dist/src/plugins/mqtt/MqttPlugin.d.ts +14 -0
- package/dist/src/plugins/mqtt/MqttPlugin.js +30 -0
- package/dist/src/plugins/mqtt/MqttPlugin.js.map +1 -0
- package/dist/src/plugins/mqtt/MqttServer.d.ts +22 -0
- package/dist/src/plugins/mqtt/MqttServer.js +109 -0
- package/dist/src/plugins/mqtt/MqttServer.js.map +1 -0
- package/dist/src/plugins/mqtt/config.schema.json +26 -0
- package/dist/src/plugins/operator/ConsistentHashRing.d.ts +20 -0
- package/dist/src/plugins/operator/ConsistentHashRing.js +64 -0
- package/dist/src/plugins/operator/ConsistentHashRing.js.map +1 -0
- package/dist/src/plugins/operator/ContractFacade.d.ts +80 -0
- package/dist/src/plugins/operator/ContractFacade.js +364 -0
- package/dist/src/plugins/operator/ContractFacade.js.map +1 -0
- package/dist/src/plugins/operator/MaintainTopologyHelper.d.ts +23 -0
- package/dist/src/plugins/operator/MaintainTopologyHelper.js +75 -0
- package/dist/src/plugins/operator/MaintainTopologyHelper.js.map +1 -0
- package/dist/src/plugins/operator/MaintainTopologyService.d.ts +11 -0
- package/dist/src/plugins/operator/MaintainTopologyService.js +57 -0
- package/dist/src/plugins/operator/MaintainTopologyService.js.map +1 -0
- package/dist/src/plugins/operator/OperatorFleetState.d.ts +33 -0
- package/dist/src/plugins/operator/OperatorFleetState.js +112 -0
- package/dist/src/plugins/operator/OperatorFleetState.js.map +1 -0
- package/dist/src/plugins/operator/OperatorPlugin.d.ts +50 -0
- package/dist/src/plugins/operator/OperatorPlugin.js +159 -0
- package/dist/src/plugins/operator/OperatorPlugin.js.map +1 -0
- package/dist/src/plugins/operator/StreamPartAssignments.d.ts +28 -0
- package/dist/src/plugins/operator/StreamPartAssignments.js +104 -0
- package/dist/src/plugins/operator/StreamPartAssignments.js.map +1 -0
- package/dist/src/plugins/operator/announceNodeToContract.d.ts +3 -0
- package/dist/src/plugins/operator/announceNodeToContract.js +39 -0
- package/dist/src/plugins/operator/announceNodeToContract.js.map +1 -0
- package/dist/src/plugins/operator/announceNodeToStream.d.ts +3 -0
- package/dist/src/plugins/operator/announceNodeToStream.js +25 -0
- package/dist/src/plugins/operator/announceNodeToStream.js.map +1 -0
- package/dist/src/plugins/operator/checkOperatorValueBreach.d.ts +2 -0
- package/dist/src/plugins/operator/checkOperatorValueBreach.js +21 -0
- package/dist/src/plugins/operator/checkOperatorValueBreach.js.map +1 -0
- package/dist/src/plugins/operator/closeExpiredFlags.d.ts +3 -0
- package/dist/src/plugins/operator/closeExpiredFlags.js +24 -0
- package/dist/src/plugins/operator/closeExpiredFlags.js.map +1 -0
- package/dist/src/plugins/operator/config.schema.json +152 -0
- package/dist/src/plugins/operator/createIsLeaderFn.d.ts +4 -0
- package/dist/src/plugins/operator/createIsLeaderFn.js +14 -0
- package/dist/src/plugins/operator/createIsLeaderFn.js.map +1 -0
- package/dist/src/plugins/operator/fetchRedundancyFactor.d.ts +2 -0
- package/dist/src/plugins/operator/fetchRedundancyFactor.js +43 -0
- package/dist/src/plugins/operator/fetchRedundancyFactor.js.map +1 -0
- package/dist/src/plugins/operator/formCoordinationStreamId.d.ts +3 -0
- package/dist/src/plugins/operator/formCoordinationStreamId.js +9 -0
- package/dist/src/plugins/operator/formCoordinationStreamId.js.map +1 -0
- package/dist/src/plugins/operator/heartbeatUtils.d.ts +67 -0
- package/dist/src/plugins/operator/heartbeatUtils.js +26 -0
- package/dist/src/plugins/operator/heartbeatUtils.js.map +1 -0
- package/dist/src/plugins/operator/inspectOverTime.d.ts +22 -0
- package/dist/src/plugins/operator/inspectOverTime.js +146 -0
- package/dist/src/plugins/operator/inspectOverTime.js.map +1 -0
- package/dist/src/plugins/operator/inspectRandomNode.d.ts +8 -0
- package/dist/src/plugins/operator/inspectRandomNode.js +44 -0
- package/dist/src/plugins/operator/inspectRandomNode.js.map +1 -0
- package/dist/src/plugins/operator/inspectionUtils.d.ts +23 -0
- package/dist/src/plugins/operator/inspectionUtils.js +120 -0
- package/dist/src/plugins/operator/inspectionUtils.js.map +1 -0
- package/dist/src/plugins/operator/maintainOperatorValue.d.ts +2 -0
- package/dist/src/plugins/operator/maintainOperatorValue.js +21 -0
- package/dist/src/plugins/operator/maintainOperatorValue.js.map +1 -0
- package/dist/src/plugins/operator/reviewSuspectNode.d.ts +24 -0
- package/dist/src/plugins/operator/reviewSuspectNode.js +56 -0
- package/dist/src/plugins/operator/reviewSuspectNode.js.map +1 -0
- package/dist/src/plugins/storage/Batch.d.ts +51 -0
- package/dist/src/plugins/storage/Batch.js +121 -0
- package/dist/src/plugins/storage/Batch.js.map +1 -0
- package/dist/src/plugins/storage/BatchManager.d.ts +27 -0
- package/dist/src/plugins/storage/BatchManager.js +117 -0
- package/dist/src/plugins/storage/BatchManager.js.map +1 -0
- package/dist/src/plugins/storage/Bucket.d.ts +23 -0
- package/dist/src/plugins/storage/Bucket.js +90 -0
- package/dist/src/plugins/storage/Bucket.js.map +1 -0
- package/dist/src/plugins/storage/BucketManager.d.ts +56 -0
- package/dist/src/plugins/storage/BucketManager.js +306 -0
- package/dist/src/plugins/storage/BucketManager.js.map +1 -0
- package/dist/src/plugins/storage/DataQueryFormat.d.ts +10 -0
- package/dist/src/plugins/storage/DataQueryFormat.js +51 -0
- package/dist/src/plugins/storage/DataQueryFormat.js.map +1 -0
- package/dist/src/plugins/storage/DeleteExpiredCmd.d.ts +28 -0
- package/dist/src/plugins/storage/DeleteExpiredCmd.js +155 -0
- package/dist/src/plugins/storage/DeleteExpiredCmd.js.map +1 -0
- package/dist/src/plugins/storage/SetMembershipSynchronizer.d.ts +33 -0
- package/dist/src/plugins/storage/SetMembershipSynchronizer.js +102 -0
- package/dist/src/plugins/storage/SetMembershipSynchronizer.js.map +1 -0
- package/dist/src/plugins/storage/Storage.d.ts +47 -0
- package/dist/src/plugins/storage/Storage.js +459 -0
- package/dist/src/plugins/storage/Storage.js.map +1 -0
- package/dist/src/plugins/storage/StorageConfig.d.ts +43 -0
- package/dist/src/plugins/storage/StorageConfig.js +83 -0
- package/dist/src/plugins/storage/StorageConfig.js.map +1 -0
- package/dist/src/plugins/storage/StorageEventListener.d.ts +17 -0
- package/dist/src/plugins/storage/StorageEventListener.js +46 -0
- package/dist/src/plugins/storage/StorageEventListener.js.map +1 -0
- package/dist/src/plugins/storage/StoragePlugin.d.ts +32 -0
- package/dist/src/plugins/storage/StoragePlugin.js +103 -0
- package/dist/src/plugins/storage/StoragePlugin.js.map +1 -0
- package/dist/src/plugins/storage/StoragePoller.d.ts +15 -0
- package/dist/src/plugins/storage/StoragePoller.js +47 -0
- package/dist/src/plugins/storage/StoragePoller.js.map +1 -0
- package/dist/src/plugins/storage/config.schema.json +88 -0
- package/dist/src/plugins/storage/dataMetadataEndpoint.d.ts +3 -0
- package/dist/src/plugins/storage/dataMetadataEndpoint.js +35 -0
- package/dist/src/plugins/storage/dataMetadataEndpoint.js.map +1 -0
- package/dist/src/plugins/storage/dataQueryEndpoint.d.ts +6 -0
- package/dist/src/plugins/storage/dataQueryEndpoint.js +181 -0
- package/dist/src/plugins/storage/dataQueryEndpoint.js.map +1 -0
- package/dist/src/plugins/storage/storageConfigEndpoint.d.ts +3 -0
- package/dist/src/plugins/storage/storageConfigEndpoint.js +31 -0
- package/dist/src/plugins/storage/storageConfigEndpoint.js.map +1 -0
- package/dist/src/plugins/subscriber/SubscriberPlugin.d.ts +16 -0
- package/dist/src/plugins/subscriber/SubscriberPlugin.js +22 -0
- package/dist/src/plugins/subscriber/SubscriberPlugin.js.map +1 -0
- package/dist/src/plugins/subscriber/config.schema.json +31 -0
- package/dist/src/plugins/websocket/Connection.d.ts +9 -0
- package/dist/src/plugins/websocket/Connection.js +45 -0
- package/dist/src/plugins/websocket/Connection.js.map +1 -0
- package/dist/src/plugins/websocket/PublishConnection.d.ts +12 -0
- package/dist/src/plugins/websocket/PublishConnection.js +46 -0
- package/dist/src/plugins/websocket/PublishConnection.js.map +1 -0
- package/dist/src/plugins/websocket/SubscribeConnection.d.ts +13 -0
- package/dist/src/plugins/websocket/SubscribeConnection.js +50 -0
- package/dist/src/plugins/websocket/SubscribeConnection.js.map +1 -0
- package/dist/src/plugins/websocket/WebsocketPlugin.d.ts +19 -0
- package/dist/src/plugins/websocket/WebsocketPlugin.js +26 -0
- package/dist/src/plugins/websocket/WebsocketPlugin.js.map +1 -0
- package/dist/src/plugins/websocket/WebsocketServer.d.ts +16 -0
- package/dist/src/plugins/websocket/WebsocketServer.js +132 -0
- package/dist/src/plugins/websocket/WebsocketServer.js.map +1 -0
- package/dist/src/plugins/websocket/config.schema.json +51 -0
- package/package.json +78 -0
- package/plugins.md +318 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.startCassandraStorage = exports.Storage = void 0;
|
|
7
|
+
const cassandra_driver_1 = require("cassandra-driver");
|
|
8
|
+
const utils_1 = require("@streamr/utils");
|
|
9
|
+
const BatchManager_1 = require("./BatchManager");
|
|
10
|
+
const stream_1 = require("stream");
|
|
11
|
+
const events_1 = require("events");
|
|
12
|
+
const stream_2 = require("stream");
|
|
13
|
+
const uuid_1 = require("uuid");
|
|
14
|
+
const merge2_1 = __importDefault(require("merge2"));
|
|
15
|
+
const BucketManager_1 = require("./BucketManager");
|
|
16
|
+
const utils_2 = require("@streamr/utils");
|
|
17
|
+
const dataQueryEndpoint_1 = require("./dataQueryEndpoint");
|
|
18
|
+
const trackerless_network_1 = require("@streamr/trackerless-network");
|
|
19
|
+
const logger = new utils_2.Logger(module);
|
|
20
|
+
const MAX_TIMESTAMP_VALUE = 8640000000000000; // https://262.ecma-international.org/5.1/#sec-15.9.1.1
|
|
21
|
+
const MAX_RESEND_LAST = 10000;
|
|
22
|
+
const bucketsToIds = (buckets) => buckets.map((bucket) => bucket.getId());
|
|
23
|
+
class Storage extends events_1.EventEmitter {
|
|
24
|
+
opts;
|
|
25
|
+
cassandraClient;
|
|
26
|
+
bucketManager;
|
|
27
|
+
batchManager;
|
|
28
|
+
pendingStores;
|
|
29
|
+
constructor(cassandraClient, opts) {
|
|
30
|
+
super();
|
|
31
|
+
const defaultOptions = {
|
|
32
|
+
useTtl: false,
|
|
33
|
+
retriesIntervalMilliseconds: 500
|
|
34
|
+
};
|
|
35
|
+
this.opts = {
|
|
36
|
+
...defaultOptions,
|
|
37
|
+
...opts
|
|
38
|
+
};
|
|
39
|
+
this.cassandraClient = cassandraClient;
|
|
40
|
+
this.bucketManager = new BucketManager_1.BucketManager(cassandraClient, opts);
|
|
41
|
+
this.batchManager = new BatchManager_1.BatchManager(cassandraClient, {
|
|
42
|
+
useTtl: this.opts.useTtl
|
|
43
|
+
});
|
|
44
|
+
this.pendingStores = new Map();
|
|
45
|
+
}
|
|
46
|
+
async store(streamMessage) {
|
|
47
|
+
logger.debug('Store message', { msgId: streamMessage.messageId });
|
|
48
|
+
const bucketId = this.bucketManager.getBucketId(streamMessage.getStreamId(), streamMessage.getStreamPartition(), streamMessage.getTimestamp());
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
if (bucketId) {
|
|
51
|
+
logger.trace('Found bucket', { bucketId });
|
|
52
|
+
const record = {
|
|
53
|
+
streamId: streamMessage.getStreamId(),
|
|
54
|
+
partition: streamMessage.getStreamPartition(),
|
|
55
|
+
timestamp: streamMessage.getTimestamp(),
|
|
56
|
+
sequenceNo: streamMessage.getSequenceNumber(),
|
|
57
|
+
publisherId: streamMessage.getPublisherId(),
|
|
58
|
+
msgChainId: streamMessage.getMsgChainId(),
|
|
59
|
+
payload: Buffer.from((0, trackerless_network_1.convertStreamMessageToBytes)(streamMessage))
|
|
60
|
+
};
|
|
61
|
+
this.bucketManager.incrementBucket(bucketId, record.payload.length);
|
|
62
|
+
setImmediate(() => this.batchManager.store(bucketId, record, (err) => {
|
|
63
|
+
if (err) {
|
|
64
|
+
reject(err);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.emit('write', record.payload);
|
|
68
|
+
resolve(true);
|
|
69
|
+
}
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
logger.trace('Move message to pending messages (bucket not found)', {
|
|
74
|
+
messageId: JSON.stringify(streamMessage.messageId)
|
|
75
|
+
});
|
|
76
|
+
const uuid = (0, uuid_1.v1)();
|
|
77
|
+
const timeout = setTimeout(() => {
|
|
78
|
+
this.pendingStores.delete(uuid);
|
|
79
|
+
this.store(streamMessage).then(resolve, reject);
|
|
80
|
+
}, this.opts.retriesIntervalMilliseconds);
|
|
81
|
+
this.pendingStores.set(uuid, timeout);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
requestLast(streamId, partition, limit) {
|
|
86
|
+
if (limit > MAX_RESEND_LAST) {
|
|
87
|
+
// eslint-disable-next-line no-param-reassign
|
|
88
|
+
limit = MAX_RESEND_LAST;
|
|
89
|
+
}
|
|
90
|
+
const GET_LAST_N_MESSAGES = 'SELECT payload FROM stream_data WHERE '
|
|
91
|
+
+ 'stream_id = ? AND partition = ? AND bucket_id IN ? '
|
|
92
|
+
+ 'ORDER BY ts DESC, sequence_no DESC '
|
|
93
|
+
+ 'LIMIT ?';
|
|
94
|
+
const COUNT_MESSAGES = 'SELECT COUNT(*) AS total FROM stream_data WHERE stream_id = ? AND partition = ? AND bucket_id = ?';
|
|
95
|
+
const GET_BUCKETS = 'SELECT id FROM bucket WHERE stream_id = ? AND partition = ?';
|
|
96
|
+
let total = 0;
|
|
97
|
+
const options = {
|
|
98
|
+
prepare: true, fetchSize: 1
|
|
99
|
+
};
|
|
100
|
+
const resultStream = this.createResultStream({ streamId, partition, limit });
|
|
101
|
+
const makeLastQuery = async (bucketIds) => {
|
|
102
|
+
try {
|
|
103
|
+
const params = [streamId, partition, bucketIds, limit];
|
|
104
|
+
const resultSet = await this.cassandraClient.execute(GET_LAST_N_MESSAGES, params, {
|
|
105
|
+
prepare: true,
|
|
106
|
+
fetchSize: 0 // disable paging
|
|
107
|
+
});
|
|
108
|
+
resultSet.rows.reverse().forEach((r) => {
|
|
109
|
+
resultStream.write(r);
|
|
110
|
+
});
|
|
111
|
+
resultStream.end();
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
resultStream.destroy(err);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
let bucketId;
|
|
118
|
+
const bucketIds = [];
|
|
119
|
+
/**
|
|
120
|
+
* Process:
|
|
121
|
+
* - get latest bucketId => count number of messages in this bucket
|
|
122
|
+
* - if enough => get all messages and return
|
|
123
|
+
* - if not => move to the next bucket and repeat cycle
|
|
124
|
+
*/
|
|
125
|
+
this.cassandraClient.eachRow(GET_BUCKETS, [streamId, partition], options, (_n, row) => {
|
|
126
|
+
bucketId = row.id;
|
|
127
|
+
bucketIds.push(bucketId);
|
|
128
|
+
}, async (err, result) => {
|
|
129
|
+
// do nothing if resultStream ended
|
|
130
|
+
if (resultStream.writableEnded || resultStream.readableEnded) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (err) {
|
|
134
|
+
resultStream.destroy(err);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// no buckets found at all
|
|
138
|
+
if (!bucketId) {
|
|
139
|
+
resultStream.end();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
// get total stored message in bucket
|
|
144
|
+
const resultSet = await this.cassandraClient.execute(COUNT_MESSAGES, [streamId, partition, bucketId], {
|
|
145
|
+
prepare: true,
|
|
146
|
+
fetchSize: 0 // disable paging
|
|
147
|
+
});
|
|
148
|
+
const row = resultSet.first();
|
|
149
|
+
total += row.total.low;
|
|
150
|
+
// if not enough messages and we next page exists, repeat eachRow
|
|
151
|
+
if (result.nextPage && total < limit && total < MAX_RESEND_LAST) {
|
|
152
|
+
result.nextPage();
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
makeLastQuery(bucketIds);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
resultStream.destroy(err);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
return resultStream;
|
|
164
|
+
}
|
|
165
|
+
requestFrom(streamId, partition, fromTimestamp, fromSequenceNo, publisherId) {
|
|
166
|
+
return this.fetchRange(streamId, partition, fromTimestamp, fromSequenceNo, MAX_TIMESTAMP_VALUE, dataQueryEndpoint_1.MAX_SEQUENCE_NUMBER_VALUE, publisherId);
|
|
167
|
+
}
|
|
168
|
+
requestRange(streamId, partition, fromTimestamp, fromSequenceNo, toTimestamp, toSequenceNo, publisherId, msgChainId) {
|
|
169
|
+
// TODO is there any reason why we shouldn't allow range queries which contain publisherId, but not msgChainId?
|
|
170
|
+
// (or maybe even queries with msgChain but without publisherId)
|
|
171
|
+
const isValidRequest = (publisherId !== undefined && msgChainId !== undefined) || (publisherId === undefined && msgChainId === undefined);
|
|
172
|
+
if (!isValidRequest) {
|
|
173
|
+
throw new Error('Invalid combination of requestFrom arguments');
|
|
174
|
+
}
|
|
175
|
+
return this.fetchRange(streamId, partition, fromTimestamp, fromSequenceNo, toTimestamp, toSequenceNo, publisherId, msgChainId);
|
|
176
|
+
}
|
|
177
|
+
enableMetrics(metricsContext) {
|
|
178
|
+
const metrics = {
|
|
179
|
+
readMessagesPerSecond: new utils_1.RateMetric(),
|
|
180
|
+
readBytesPerSecond: new utils_1.RateMetric(),
|
|
181
|
+
writeMessagesPerSecond: new utils_1.RateMetric(),
|
|
182
|
+
writeBytesPerSecond: new utils_1.RateMetric()
|
|
183
|
+
};
|
|
184
|
+
metricsContext.addMetrics('broker.plugin.storage', metrics);
|
|
185
|
+
this.on('read', (streamMessage) => {
|
|
186
|
+
metrics.readMessagesPerSecond.record(1);
|
|
187
|
+
metrics.readBytesPerSecond.record(streamMessage.length);
|
|
188
|
+
});
|
|
189
|
+
this.on('write', (streamMessage) => {
|
|
190
|
+
metrics.writeMessagesPerSecond.record(1);
|
|
191
|
+
metrics.writeBytesPerSecond.record(streamMessage.length);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
close() {
|
|
195
|
+
const keys = [...this.pendingStores.keys()];
|
|
196
|
+
keys.forEach((key) => {
|
|
197
|
+
const timeout = this.pendingStores.get(key);
|
|
198
|
+
clearTimeout(timeout);
|
|
199
|
+
this.pendingStores.delete(key);
|
|
200
|
+
});
|
|
201
|
+
this.bucketManager.stop();
|
|
202
|
+
this.batchManager.stop();
|
|
203
|
+
return this.cassandraClient.shutdown();
|
|
204
|
+
}
|
|
205
|
+
fetchRange(streamId, partition, fromTimestamp, fromSequenceNo, toTimestamp, toSequenceNo, publisherId, msgChainId) {
|
|
206
|
+
const resultStream = this.createResultStream({
|
|
207
|
+
streamId,
|
|
208
|
+
partition,
|
|
209
|
+
fromTimestamp,
|
|
210
|
+
fromSequenceNo,
|
|
211
|
+
toTimestamp,
|
|
212
|
+
toSequenceNo,
|
|
213
|
+
publisherId,
|
|
214
|
+
msgChainId,
|
|
215
|
+
});
|
|
216
|
+
this.bucketManager.getBucketsByTimestamp(streamId, partition, fromTimestamp, toTimestamp).then((buckets) => {
|
|
217
|
+
if (buckets.length === 0) {
|
|
218
|
+
resultStream.end();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const bucketIds = bucketsToIds(buckets);
|
|
222
|
+
let queries;
|
|
223
|
+
// optimize the typical case where the sequenceNumber doesn't filter out anything
|
|
224
|
+
if ((fromSequenceNo === dataQueryEndpoint_1.MIN_SEQUENCE_NUMBER_VALUE) && (toSequenceNo === dataQueryEndpoint_1.MAX_SEQUENCE_NUMBER_VALUE)) {
|
|
225
|
+
queries = [
|
|
226
|
+
{
|
|
227
|
+
where: 'WHERE stream_id = ? AND partition = ? AND bucket_id IN ? AND ts >= ? AND ts <= ?',
|
|
228
|
+
params: [streamId, partition, bucketIds, fromTimestamp, toTimestamp]
|
|
229
|
+
}
|
|
230
|
+
];
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
queries = [
|
|
234
|
+
{
|
|
235
|
+
where: 'WHERE stream_id = ? AND partition = ? AND bucket_id IN ? AND ts = ? AND sequence_no >= ?',
|
|
236
|
+
params: [streamId, partition, bucketIds, fromTimestamp, fromSequenceNo]
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
where: 'WHERE stream_id = ? AND partition = ? AND bucket_id IN ? AND ts > ? AND ts < ?',
|
|
240
|
+
params: [streamId, partition, bucketIds, fromTimestamp, toTimestamp]
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
where: 'WHERE stream_id = ? AND partition = ? AND bucket_id IN ? AND ts = ? AND sequence_no <= ?',
|
|
244
|
+
params: [streamId, partition, bucketIds, toTimestamp, toSequenceNo]
|
|
245
|
+
}
|
|
246
|
+
];
|
|
247
|
+
}
|
|
248
|
+
queries.forEach((q) => {
|
|
249
|
+
if (publisherId !== undefined) {
|
|
250
|
+
q.where += ' AND publisher_id = ?';
|
|
251
|
+
q.params.push(publisherId);
|
|
252
|
+
}
|
|
253
|
+
if (msgChainId !== undefined) {
|
|
254
|
+
q.where += ' AND msg_chain_id = ?';
|
|
255
|
+
q.params.push(msgChainId);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
const streams = queries.map((q) => {
|
|
259
|
+
const select = `SELECT payload FROM stream_data ${q.where} ALLOW FILTERING`;
|
|
260
|
+
return this.queryWithStreamingResults(select, q.params);
|
|
261
|
+
});
|
|
262
|
+
return (0, stream_2.pipeline)(
|
|
263
|
+
// @ts-expect-error options not in type
|
|
264
|
+
(0, merge2_1.default)(...streams, {
|
|
265
|
+
pipeError: true,
|
|
266
|
+
}), resultStream, (err) => {
|
|
267
|
+
if (err) {
|
|
268
|
+
resultStream.destroy(err);
|
|
269
|
+
streams.forEach((s) => s.destroy(undefined));
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
})
|
|
273
|
+
.catch((e) => {
|
|
274
|
+
resultStream.destroy(e);
|
|
275
|
+
});
|
|
276
|
+
return resultStream;
|
|
277
|
+
}
|
|
278
|
+
queryWithStreamingResults(query, queryParams) {
|
|
279
|
+
return this.cassandraClient.stream(query, queryParams, {
|
|
280
|
+
prepare: true,
|
|
281
|
+
// force small page sizes, otherwise gives RangeError [ERR_OUT_OF_RANGE]: The value of "offset" is out of range.
|
|
282
|
+
fetchSize: 128,
|
|
283
|
+
readTimeout: 0,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
parseRow(row, debugInfo) {
|
|
287
|
+
if (row.payload === null) {
|
|
288
|
+
logger.error('Found unexpected message with NULL payload in Cassandra', { debugInfo });
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
this.emit('read', row.payload);
|
|
292
|
+
return row.payload;
|
|
293
|
+
}
|
|
294
|
+
createResultStream(debugInfo) {
|
|
295
|
+
const self = this; // eslint-disable-line @typescript-eslint/no-this-alias
|
|
296
|
+
let last = Date.now();
|
|
297
|
+
return new stream_1.Transform({
|
|
298
|
+
highWaterMark: 1024, // buffer up to 1024 messages
|
|
299
|
+
objectMode: true,
|
|
300
|
+
transform(row, _, done) {
|
|
301
|
+
const now = Date.now();
|
|
302
|
+
const message = self.parseRow(row, debugInfo);
|
|
303
|
+
if (message !== null) {
|
|
304
|
+
this.push(message);
|
|
305
|
+
}
|
|
306
|
+
// To avoid blocking main thread for too long, after every 100ms
|
|
307
|
+
// pause & resume the cassandraStream to give other events in the event
|
|
308
|
+
// queue a chance to be handled.
|
|
309
|
+
if ((now - last) > 100) {
|
|
310
|
+
setImmediate(() => {
|
|
311
|
+
last = Date.now();
|
|
312
|
+
done();
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
done();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
async getFirstMessageTimestampInStream(streamId, partition) {
|
|
322
|
+
const bucketQuery = 'SELECT id FROM bucket WHERE stream_id=? AND partition =? ORDER BY date_create ASC LIMIT 1';
|
|
323
|
+
const queryParams = [streamId, partition];
|
|
324
|
+
const buckets = await this.cassandraClient.execute(bucketQuery, queryParams, {
|
|
325
|
+
prepare: true,
|
|
326
|
+
});
|
|
327
|
+
if (buckets.rows.length !== 1) {
|
|
328
|
+
return 0;
|
|
329
|
+
}
|
|
330
|
+
const bucketId = buckets.rows[0].id;
|
|
331
|
+
const query = 'SELECT ts FROM stream_data WHERE stream_id=? AND partition=? AND bucket_id=? ORDER BY ts ASC LIMIT 1';
|
|
332
|
+
const streams = await this.cassandraClient.execute(query, [
|
|
333
|
+
streamId,
|
|
334
|
+
partition,
|
|
335
|
+
bucketId
|
|
336
|
+
], {
|
|
337
|
+
prepare: true
|
|
338
|
+
});
|
|
339
|
+
if (streams.rows.length !== 1) {
|
|
340
|
+
return 0;
|
|
341
|
+
}
|
|
342
|
+
const { ts } = streams.rows[0];
|
|
343
|
+
return new Date(ts).getTime();
|
|
344
|
+
}
|
|
345
|
+
async getLastMessageTimestampInStream(streamId, partition) {
|
|
346
|
+
const bucketQuery = 'SELECT id FROM bucket WHERE stream_id=? AND partition =? ORDER BY date_create DESC LIMIT 1';
|
|
347
|
+
const queryParams = [streamId, partition];
|
|
348
|
+
const buckets = await this.cassandraClient.execute(bucketQuery, queryParams, {
|
|
349
|
+
prepare: true,
|
|
350
|
+
});
|
|
351
|
+
if (buckets.rows.length !== 1) {
|
|
352
|
+
return 0;
|
|
353
|
+
}
|
|
354
|
+
const bucketId = buckets.rows[0].id;
|
|
355
|
+
const query = 'SELECT ts FROM stream_data WHERE stream_id=? AND partition=? AND bucket_id=? ORDER BY ts DESC LIMIT 1';
|
|
356
|
+
const streams = await this.cassandraClient.execute(query, [
|
|
357
|
+
streamId,
|
|
358
|
+
partition,
|
|
359
|
+
bucketId
|
|
360
|
+
], {
|
|
361
|
+
prepare: true
|
|
362
|
+
});
|
|
363
|
+
if (streams.rows.length !== 1) {
|
|
364
|
+
return 0;
|
|
365
|
+
}
|
|
366
|
+
const { ts } = streams.rows[0];
|
|
367
|
+
return new Date(ts).getTime();
|
|
368
|
+
}
|
|
369
|
+
async getNumberOfMessagesInStream(streamId, partition) {
|
|
370
|
+
const query = 'SELECT SUM(records) as count FROM bucket WHERE stream_id=? AND partition=?';
|
|
371
|
+
const queryParams = [
|
|
372
|
+
streamId,
|
|
373
|
+
partition
|
|
374
|
+
];
|
|
375
|
+
const res = await this.cassandraClient.execute(query, queryParams, {
|
|
376
|
+
prepare: true
|
|
377
|
+
});
|
|
378
|
+
if (res.rows.length !== 1) {
|
|
379
|
+
return 0;
|
|
380
|
+
}
|
|
381
|
+
const { count } = res.rows[0];
|
|
382
|
+
return count;
|
|
383
|
+
}
|
|
384
|
+
async getTotalBytesInStream(streamId, partition) {
|
|
385
|
+
const query = 'SELECT SUM(size) as count FROM bucket WHERE stream_id=? AND partition=?';
|
|
386
|
+
const queryParams = [
|
|
387
|
+
streamId,
|
|
388
|
+
partition
|
|
389
|
+
];
|
|
390
|
+
const res = await this.cassandraClient.execute(query, queryParams, {
|
|
391
|
+
prepare: true
|
|
392
|
+
});
|
|
393
|
+
if (res.rows.length !== 1) {
|
|
394
|
+
return 0;
|
|
395
|
+
}
|
|
396
|
+
let { count } = res.rows[0];
|
|
397
|
+
// Cassandra's integer has overflown, calculate fetching row by row
|
|
398
|
+
if (count < 0) {
|
|
399
|
+
count = 0;
|
|
400
|
+
const query = 'SELECT size FROM bucket WHERE stream_id=? AND partition=?';
|
|
401
|
+
const queryParams = [
|
|
402
|
+
streamId,
|
|
403
|
+
partition
|
|
404
|
+
];
|
|
405
|
+
const res = await this.cassandraClient.execute(query, queryParams, {
|
|
406
|
+
prepare: true
|
|
407
|
+
});
|
|
408
|
+
for (const row of res.rows) {
|
|
409
|
+
count += row.size;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return count;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
exports.Storage = Storage;
|
|
416
|
+
function sleep(ms) {
|
|
417
|
+
return new Promise((resolve) => setTimeout(() => resolve(undefined), ms));
|
|
418
|
+
}
|
|
419
|
+
const startCassandraStorage = async ({ contactPoints, localDataCenter, keyspace, username, password, opts }) => {
|
|
420
|
+
const authProvider = new cassandra_driver_1.auth.PlainTextAuthProvider(username || '', password || '');
|
|
421
|
+
const requestLogger = new cassandra_driver_1.tracker.RequestLogger({
|
|
422
|
+
slowThreshold: 10 * 1000, // 10 secs
|
|
423
|
+
});
|
|
424
|
+
// @ts-expect-error 'emitter' field is missing in type definition file
|
|
425
|
+
requestLogger.emitter.on('slow', (message) => {
|
|
426
|
+
logger.warn('Encountered "slow" event from cassandraClient', { message });
|
|
427
|
+
});
|
|
428
|
+
const cassandraClient = new cassandra_driver_1.Client({
|
|
429
|
+
contactPoints,
|
|
430
|
+
localDataCenter,
|
|
431
|
+
keyspace,
|
|
432
|
+
authProvider,
|
|
433
|
+
requestTracker: requestLogger,
|
|
434
|
+
pooling: {
|
|
435
|
+
maxRequestsPerConnection: 32768
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
const nbTrials = 20;
|
|
439
|
+
let retryCount = nbTrials;
|
|
440
|
+
let lastError = '';
|
|
441
|
+
while (retryCount > 0) {
|
|
442
|
+
/* eslint-disable no-await-in-loop */
|
|
443
|
+
try {
|
|
444
|
+
await cassandraClient.connect().catch((err) => { throw err; });
|
|
445
|
+
return new Storage(cassandraClient, opts || {});
|
|
446
|
+
}
|
|
447
|
+
catch (err) {
|
|
448
|
+
// eslint-disable-next-line no-console
|
|
449
|
+
console.log('Cassandra not responding yet...');
|
|
450
|
+
retryCount -= 1;
|
|
451
|
+
await sleep(5000);
|
|
452
|
+
lastError = err;
|
|
453
|
+
}
|
|
454
|
+
/* eslint-enable no-await-in-loop */
|
|
455
|
+
}
|
|
456
|
+
throw new Error(`Failed to connect to Cassandra after ${nbTrials} trials: ${lastError.toString()}`);
|
|
457
|
+
};
|
|
458
|
+
exports.startCassandraStorage = startCassandraStorage;
|
|
459
|
+
//# sourceMappingURL=Storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Storage.js","sourceRoot":"","sources":["../../../../src/plugins/storage/Storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { EthereumAddress } from '@streamr/utils';
|
|
2
|
+
import { StreamPartID } from '@streamr/protocol';
|
|
3
|
+
import { StreamrClient } from '@streamr/sdk';
|
|
4
|
+
export interface StorageConfigListener {
|
|
5
|
+
onStreamPartAdded: (streamPart: StreamPartID) => void;
|
|
6
|
+
onStreamPartRemoved: (streamPart: StreamPartID) => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Manages the two data sources for storage node assignments (poll-based and
|
|
10
|
+
* event-based), feeding the received full state and partial state updates to
|
|
11
|
+
* `StorageAssignmentSynchronizer`. The state diffs produced by the
|
|
12
|
+
* synchronizer are then further delivered to the user of this class via
|
|
13
|
+
* listeners.
|
|
14
|
+
*
|
|
15
|
+
* The two data sources, heterogeneous in nature, are:
|
|
16
|
+
*
|
|
17
|
+
* (1) Poll-based storage node assignments occurring on a scheduled interval
|
|
18
|
+
* (reliable, large payload, infrequent, may be stale)
|
|
19
|
+
*
|
|
20
|
+
* (2) Event-based storage node assignments picked up in real-time
|
|
21
|
+
* (intermittent, small payload, frequent, up-to-date)
|
|
22
|
+
*
|
|
23
|
+
* Event-based assignments are great for picking up on changes quickly.
|
|
24
|
+
* However, there is a risk of not receiving updates due to, e.g. connectivity
|
|
25
|
+
* issues. Therefore, if the real-time system fails, polling acts as a sort-of
|
|
26
|
+
* backup system.
|
|
27
|
+
*/
|
|
28
|
+
export declare class StorageConfig {
|
|
29
|
+
private readonly listener;
|
|
30
|
+
private readonly synchronizer;
|
|
31
|
+
private readonly clusterSize;
|
|
32
|
+
private readonly myIndexInCluster;
|
|
33
|
+
private readonly storagePoller;
|
|
34
|
+
private readonly storageEventListener;
|
|
35
|
+
private readonly abortController;
|
|
36
|
+
constructor(clusterId: EthereumAddress, clusterSize: number, myIndexInCluster: number, pollInterval: number, streamrClient: StreamrClient, listener: StorageConfigListener);
|
|
37
|
+
start(): Promise<void>;
|
|
38
|
+
destroy(): void;
|
|
39
|
+
hasStreamPart(streamPart: StreamPartID): boolean;
|
|
40
|
+
getStreamParts(): ReadonlySet<StreamPartID>;
|
|
41
|
+
private createMyStreamParts;
|
|
42
|
+
private handleDiff;
|
|
43
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StorageConfig = void 0;
|
|
4
|
+
const utils_1 = require("@streamr/utils");
|
|
5
|
+
const SetMembershipSynchronizer_1 = require("./SetMembershipSynchronizer");
|
|
6
|
+
const StoragePoller_1 = require("./StoragePoller");
|
|
7
|
+
const StorageEventListener_1 = require("./StorageEventListener");
|
|
8
|
+
const logger = new utils_1.Logger(module);
|
|
9
|
+
/**
|
|
10
|
+
* Manages the two data sources for storage node assignments (poll-based and
|
|
11
|
+
* event-based), feeding the received full state and partial state updates to
|
|
12
|
+
* `StorageAssignmentSynchronizer`. The state diffs produced by the
|
|
13
|
+
* synchronizer are then further delivered to the user of this class via
|
|
14
|
+
* listeners.
|
|
15
|
+
*
|
|
16
|
+
* The two data sources, heterogeneous in nature, are:
|
|
17
|
+
*
|
|
18
|
+
* (1) Poll-based storage node assignments occurring on a scheduled interval
|
|
19
|
+
* (reliable, large payload, infrequent, may be stale)
|
|
20
|
+
*
|
|
21
|
+
* (2) Event-based storage node assignments picked up in real-time
|
|
22
|
+
* (intermittent, small payload, frequent, up-to-date)
|
|
23
|
+
*
|
|
24
|
+
* Event-based assignments are great for picking up on changes quickly.
|
|
25
|
+
* However, there is a risk of not receiving updates due to, e.g. connectivity
|
|
26
|
+
* issues. Therefore, if the real-time system fails, polling acts as a sort-of
|
|
27
|
+
* backup system.
|
|
28
|
+
*/
|
|
29
|
+
class StorageConfig {
|
|
30
|
+
listener;
|
|
31
|
+
synchronizer = new SetMembershipSynchronizer_1.SetMembershipSynchronizer();
|
|
32
|
+
clusterSize;
|
|
33
|
+
myIndexInCluster;
|
|
34
|
+
storagePoller;
|
|
35
|
+
storageEventListener;
|
|
36
|
+
abortController;
|
|
37
|
+
constructor(clusterId, clusterSize, myIndexInCluster, pollInterval, streamrClient, listener) {
|
|
38
|
+
this.clusterSize = clusterSize;
|
|
39
|
+
this.myIndexInCluster = myIndexInCluster;
|
|
40
|
+
this.listener = listener;
|
|
41
|
+
this.storagePoller = new StoragePoller_1.StoragePoller(clusterId, pollInterval, streamrClient, (streams, block) => {
|
|
42
|
+
const streamParts = streams.flatMap((stream) => ([
|
|
43
|
+
...this.createMyStreamParts(stream)
|
|
44
|
+
]));
|
|
45
|
+
this.handleDiff(this.synchronizer.ingestSnapshot(new Set(streamParts), block));
|
|
46
|
+
});
|
|
47
|
+
this.storageEventListener = new StorageEventListener_1.StorageEventListener(clusterId, streamrClient, (stream, type, block) => {
|
|
48
|
+
const streamParts = this.createMyStreamParts(stream);
|
|
49
|
+
this.handleDiff(this.synchronizer.ingestPatch(streamParts, type, block));
|
|
50
|
+
});
|
|
51
|
+
this.abortController = new AbortController();
|
|
52
|
+
}
|
|
53
|
+
async start() {
|
|
54
|
+
this.storageEventListener.start();
|
|
55
|
+
await this.storagePoller.start(this.abortController.signal);
|
|
56
|
+
}
|
|
57
|
+
destroy() {
|
|
58
|
+
this.abortController.abort();
|
|
59
|
+
this.storageEventListener.destroy();
|
|
60
|
+
}
|
|
61
|
+
hasStreamPart(streamPart) {
|
|
62
|
+
return this.getStreamParts().has(streamPart);
|
|
63
|
+
}
|
|
64
|
+
getStreamParts() {
|
|
65
|
+
return this.synchronizer.getState();
|
|
66
|
+
}
|
|
67
|
+
createMyStreamParts(stream) {
|
|
68
|
+
return new Set(stream.getStreamParts().filter((streamPart) => {
|
|
69
|
+
const hashedIndex = (0, utils_1.keyToArrayIndex)(this.clusterSize, streamPart);
|
|
70
|
+
return hashedIndex === this.myIndexInCluster;
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
handleDiff({ added, removed }) {
|
|
74
|
+
added.forEach((streamPart) => this.listener.onStreamPartAdded(streamPart));
|
|
75
|
+
removed.forEach((streamPart) => this.listener.onStreamPartRemoved(streamPart));
|
|
76
|
+
logger.info('Updated state', {
|
|
77
|
+
added,
|
|
78
|
+
removed
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.StorageConfig = StorageConfig;
|
|
83
|
+
//# sourceMappingURL=StorageConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StorageConfig.js","sourceRoot":"","sources":["../../../../src/plugins/storage/StorageConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Stream, StreamrClient } from '@streamr/sdk';
|
|
2
|
+
import { EthereumAddress } from '@streamr/utils';
|
|
3
|
+
/**
|
|
4
|
+
* Hooks up to StreamrClient event listener to learn about
|
|
5
|
+
* stream assignment and removal events in real-time.
|
|
6
|
+
*/
|
|
7
|
+
export declare class StorageEventListener {
|
|
8
|
+
private readonly clusterId;
|
|
9
|
+
private readonly streamrClient;
|
|
10
|
+
private readonly onEvent;
|
|
11
|
+
private readonly onAddToStorageNode;
|
|
12
|
+
private readonly onRemoveFromStorageNode;
|
|
13
|
+
constructor(clusterId: EthereumAddress, streamrClient: StreamrClient, onEvent: (stream: Stream, type: 'added' | 'removed', block: number) => void);
|
|
14
|
+
private handleEvent;
|
|
15
|
+
start(): void;
|
|
16
|
+
destroy(): void;
|
|
17
|
+
}
|