Skip to content

Commit 9ae7e8e

Browse files
author
web3blind
committed
Add Viz Magic archive indexer daemon
1 parent e2b7cc2 commit 9ae7e8e

4 files changed

Lines changed: 112 additions & 1 deletion

File tree

tools/archive-node/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ For a small catch-up run from the saved cursor:
3131
node indexer.js --max-blocks 500
3232
```
3333

34+
For continuous low-memory indexing of new blocks:
35+
36+
```bash
37+
node daemon.js
38+
```
39+
3440
## HTTP API
3541

3642
```text

tools/archive-node/config.example.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@
1010
"batchSize": 50,
1111
"requestDelayMs": 120,
1212
"timeoutMs": 8000,
13-
"maxBlocksPerRun": 0
13+
"maxBlocksPerRun": 0,
14+
"pollIntervalMs": 5000,
15+
"maxBlocksPerTick": 500
1416
}

tools/archive-node/daemon.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
'use strict';
2+
3+
var path = require('path');
4+
var indexer = require('./indexer');
5+
6+
function sleep(ms) {
7+
return new Promise(function(resolve) { setTimeout(resolve, ms); });
8+
}
9+
10+
function loadDaemonConfig() {
11+
var configPath = process.env.ARCHIVE_NODE_CONFIG || path.join(__dirname, 'config.json');
12+
var cfg = indexer.loadConfig(configPath);
13+
cfg.pollIntervalMs = Number(process.env.ARCHIVE_NODE_POLL_MS || cfg.pollIntervalMs || 5000);
14+
cfg.maxBlocksPerTick = Number(process.env.ARCHIVE_NODE_MAX_BLOCKS_PER_TICK || cfg.maxBlocksPerTick || 500);
15+
return cfg;
16+
}
17+
18+
function rpcCall(nodeUrl, method, params, timeoutMs) {
19+
return new Promise(function(resolve, reject) {
20+
var controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
21+
var done = false;
22+
var timer = setTimeout(function() {
23+
if (controller) controller.abort();
24+
if (!done) reject(new Error('timeout'));
25+
}, timeoutMs || 8000);
26+
fetch(nodeUrl, {
27+
method: 'POST',
28+
headers: { 'Content-Type': 'application/json' },
29+
body: JSON.stringify({ jsonrpc: '2.0', method: 'call', params: ['database_api', method, params || []], id: 1 }),
30+
signal: controller ? controller.signal : undefined
31+
}).then(function(resp) {
32+
if (!resp.ok) throw new Error('HTTP ' + resp.status);
33+
return resp.json();
34+
}).then(function(payload) {
35+
done = true;
36+
clearTimeout(timer);
37+
if (!payload || !payload.result) throw new Error('empty result');
38+
resolve(payload.result);
39+
}).catch(function(err) {
40+
done = true;
41+
clearTimeout(timer);
42+
reject(err);
43+
});
44+
});
45+
}
46+
47+
async function getHeadBlock(cfg) {
48+
var lastErr = null;
49+
for (var i = 0; i < cfg.sourceNodes.length; i += 1) {
50+
try {
51+
var dgp = await rpcCall(cfg.sourceNodes[i], 'get_dynamic_global_properties', [], cfg.timeoutMs);
52+
return Number(dgp.head_block_number || 0);
53+
} catch (err) {
54+
lastErr = err;
55+
}
56+
}
57+
throw lastErr || new Error('cannot read head block');
58+
}
59+
60+
async function runForever() {
61+
var cfg = loadDaemonConfig();
62+
console.log('archive-node daemon started pollMs=' + cfg.pollIntervalMs + ' maxBlocksPerTick=' + cfg.maxBlocksPerTick);
63+
while (true) {
64+
try {
65+
var head = await getHeadBlock(cfg);
66+
var result = await indexer.indexRange({ config: cfg, to: head, maxBlocks: cfg.maxBlocksPerTick, once: true });
67+
if (result.indexedBlocks || result.indexedEvents) {
68+
console.log('archive-node tick head=' + head + ' indexedBlocks=' + result.indexedBlocks + ' indexedEvents=' + result.indexedEvents + ' last=' + result.lastIndexedBlock);
69+
}
70+
} catch (err) {
71+
console.error('archive-node tick failed: ' + (err && err.stack || err));
72+
}
73+
await sleep(cfg.pollIntervalMs);
74+
}
75+
}
76+
77+
if (require.main === module) {
78+
runForever().catch(function(err) {
79+
console.error(err && err.stack || err);
80+
process.exit(1);
81+
});
82+
}
83+
84+
module.exports = {
85+
loadDaemonConfig: loadDaemonConfig,
86+
rpcCall: rpcCall,
87+
getHeadBlock: getHeadBlock,
88+
runForever: runForever
89+
};

tools/archive-node/package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "viz-magic-archive-node",
3+
"version": "0.1.0",
4+
"private": true,
5+
"description": "Read-only Viz Magic game archive indexer and API",
6+
"main": "server.js",
7+
"scripts": {
8+
"index": "node indexer.js",
9+
"serve": "node server.js",
10+
"daemon": "node daemon.js",
11+
"test": "node archive-node.test.js"
12+
},
13+
"license": "MIT"
14+
}

0 commit comments

Comments
 (0)