substreams-sink-redis
Version:
Substreams Redis sink module
112 lines • 4.96 kB
JavaScript
import { TimeSeriesAggregationType } from "redis";
import { TS_ADD, SET, TS_CREATE, TS_CREATERULE } from "./redis.js";
import { parseKey, toTimestamp } from "./utils.js";
import { logger } from "substreams-sink";
export async function handleOutput(client, message, cursor, clock, options) {
const type = await message.getType();
switch (type.typeName.toString()) {
case "pinax.substreams.sink.prometheus.v1.PrometheusOperations":
return handlePrometheusOperations(client, message, clock, options);
case "sf.substreams.sink.kv.v1.KVOperations":
return handleKVOperations(client, message, clock, options);
}
}
export function handleClock(client, clock, options) {
return Promise.all([
SET(client, parseKey("clock:timestamp", options), toTimestamp(clock)),
SET(client, parseKey("clock:number", options), Number(clock.number))
]);
}
export function handleCursor(client, cursor, options) {
return SET(client, parseKey("cursor", options), cursor);
}
export async function handlePrometheusOperations(client, message, clock, options) {
const operations = message?.operations || [];
return Promise.all(operations.map(operation => {
handlePrometheusOperation(client, operation, clock, options);
}));
}
// global cache, stores all newly created keys
const keys = new Set();
// global cache, stores all newly created rules
const rules = new Set();
export async function createRules(client, key, options, labels) {
if (options.kvCreateRules) {
const destinationKey = `${key}:${options.kvBucketDuration}:sum`;
// Destination key and rule already created, skip
if (keys.has(destinationKey))
return;
// Create source key
if (!keys.has(key) && !await client.EXISTS(key)) {
try {
await TS_CREATE(client, key, labels, options.kvRetentionPeriod);
keys.add(key);
}
catch (e) {
logger.warn(`Failed to create key ${key}`, e);
}
}
// Create destination key
if (!keys.has(destinationKey) && !await client.EXISTS(destinationKey)) {
try {
await TS_CREATE(client, destinationKey, labels, 0); // does not include retention period
keys.add(destinationKey);
}
catch (e) {
logger.warn(`Failed to create destination key ${key}`, e);
}
}
else if (!keys.has(destinationKey) && await client.EXISTS(destinationKey)) {
keys.add(destinationKey);
return;
}
try {
await TS_CREATERULE(client, key, destinationKey, options);
}
catch (e) {
logger.warn(`Failed to create rule for key ${key}`, e);
}
}
}
export async function handlePrometheusOperation(client, operation, clock, options) {
const key = parseKey(operation.name, options, operation.labels);
await createRules(client, key, options, operation.labels);
switch (operation.operation.case) {
case "counter":
return await handlePrometheusCounter(client, key, operation.toJson(), clock, options);
case "gauge":
return await handlePrometheusGauge(client, key, operation.toJson(), clock, options);
}
}
export async function handlePrometheusCounter(client, key, operation, clock, options) {
// https://github.com/pinax-network/substreams-sink-prometheus.rs/blob/main/proto/substreams/sink/prometheus/v1/prometheus.proto#L48
switch (operation.counter.operation) {
case "OPERATION_ADD":
return await TS_ADD(client, key, operation.counter.value, clock, operation.labels, options);
case "OPERATION_INC":
return await TS_ADD(client, key, 1, clock, operation.labels, options);
}
}
export async function handlePrometheusGauge(client, key, operation, clock, options) {
// https://github.com/pinax-network/substreams-sink-prometheus.rs/blob/main/proto/substreams/sink/prometheus/v1/prometheus.proto#L23
switch (operation.gauge.operation) {
case "OPERATION_ADD":
return TS_ADD(client, key, operation.gauge.value, clock, operation.labels, options);
case "OPERATION_INC":
return TS_ADD(client, key, 1, clock, operation.labels, options);
// TO-DO: Set gauge value
// TO-DO: Remove gauge value
}
}
export async function handleKVOperations(client, message, clock, options) {
const operations = message?.operations || [];
return Promise.all(operations.map(operation => {
handleKVOperation(client, operation, clock, options);
}));
}
export async function handleKVOperation(client, operation, clock, options) {
const key = parseKey(operation.key, options);
const value = Buffer.from(operation.value).toString();
return SET(client, key, value);
}
//# sourceMappingURL=handlers.js.map