add /debug to dlobPublisher

This commit is contained in:
wphan
2024-12-02 17:38:18 -08:00
parent 09e83a6dae
commit 815c3c3ca1
7 changed files with 249 additions and 8 deletions

6
.gitignore vendored
View File

@@ -142,4 +142,8 @@ src/**.js.map
lib
src/playground.ts
*dump.rdb
# redis files
*.rdb
*.log
*.confg

View File

@@ -18,6 +18,12 @@ This is the backend server that provides a REST API for the drift [DLOB](https:/
## Setup
The build dependencies
```
git submodule update --init
bash build_all.sh
```
First set the necessary environment variables:
```
cp .env.example .env
@@ -66,18 +72,28 @@ To run the websocket server, a Redis cache is required, and the following enviro
* `REDIS_PASSWORDS`
* `REDIS_PORTS`
In one terminal, run:
In the first terminal, start the redis cluster:
```
bash redisCluster.sh start
bash redisCluster.sh create
```
In second terminal, run:
```
yarn run dlob-publisher
```
In a second terminal, run:
In a third terminal, run:
```
yarn run ws-manager
```
Then connect to the ws server at ws://127.0.0.1:3000
When you're done, stop the redis cluster:
```
bash redisCluster.sh stop
```
# Run the example client

142
redisCluster.sh Normal file
View File

@@ -0,0 +1,142 @@
#!/bin/bash
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
# Settings
CLUSTER_HOST=127.0.0.1
PORT=6378
TIMEOUT=2000
NODES=6
REPLICAS=1
PROTECTED_MODE=yes
ADDITIONAL_OPTIONS=""
# You may want to put the above config parameters into config.sh in order to
# override the defaults without modifying this script.
if [ -a config.sh ]
then
source "config.sh"
fi
# Computed vars
ENDPORT=$((PORT+NODES))
if [ "$1" == "start" ]
then
while [ $((PORT < ENDPORT)) != "0" ]; do
PORT=$((PORT+1))
echo "Starting $PORT"
redis-server --port $PORT --protected-mode $PROTECTED_MODE --cluster-enabled yes --cluster-config-file nodes-${PORT}.conf --cluster-node-timeout $TIMEOUT --appendonly yes --appendfilename appendonly-${PORT}.aof --appenddirname appendonlydir-${PORT} --dbfilename dump-${PORT}.rdb --logfile ${PORT}.log --daemonize yes ${ADDITIONAL_OPTIONS}
done
exit 0
fi
if [ "$1" == "create" ]
then
HOSTS=""
while [ $((PORT < ENDPORT)) != "0" ]; do
PORT=$((PORT+1))
HOSTS="$HOSTS $CLUSTER_HOST:$PORT"
done
OPT_ARG=""
if [ "$2" == "-f" ]; then
OPT_ARG="--cluster-yes"
fi
redis-cli --cluster create $HOSTS --cluster-replicas $REPLICAS $OPT_ARG
exit 0
fi
if [ "$1" == "stop" ]
then
while [ $((PORT < ENDPORT)) != "0" ]; do
PORT=$((PORT+1))
echo "Stopping $PORT"
redis-cli -p $PORT shutdown nosave
done
exit 0
fi
if [ "$1" == "restart" ]
then
OLD_PORT=$PORT
while [ $((PORT < ENDPORT)) != "0" ]; do
PORT=$((PORT+1))
echo "Stopping $PORT"
redis-cli -p $PORT shutdown nosave
done
PORT=$OLD_PORT
while [ $((PORT < ENDPORT)) != "0" ]; do
PORT=$((PORT+1))
echo "Starting $PORT"
redis-server --port $PORT --protected-mode $PROTECTED_MODE --cluster-enabled yes --cluster-config-file nodes-${PORT}.conf --cluster-node-timeout $TIMEOUT --appendonly yes --appendfilename appendonly-${PORT}.aof --appenddirname appendonlydir-${PORT} --dbfilename dump-${PORT}.rdb --logfile ${PORT}.log --daemonize yes ${ADDITIONAL_OPTIONS}
done
exit 0
fi
if [ "$1" == "watch" ]
then
PORT=$((PORT+1))
while [ 1 ]; do
clear
date
redis-cli -p $PORT cluster nodes | head -30
sleep 1
done
exit 0
fi
if [ "$1" == "tail" ]
then
INSTANCE=$2
PORT=$((PORT+INSTANCE))
tail -f ${PORT}.log
exit 0
fi
if [ "$1" == "tailall" ]
then
tail -f *.log
exit 0
fi
if [ "$1" == "call" ]
then
while [ $((PORT < ENDPORT)) != "0" ]; do
PORT=$((PORT+1))
redis-cli -p $PORT $2 $3 $4 $5 $6 $7 $8 $9
done
exit 0
fi
if [ "$1" == "clean" ]
then
echo "Cleaning *.log"
rm -rf *.log
echo "Cleaning appendonlydir-*"
rm -rf appendonlydir-*
echo "Cleaning dump-*.rdb"
rm -rf dump-*.rdb
echo "Cleaning nodes-*.conf"
rm -rf nodes-*.conf
exit 0
fi
if [ "$1" == "clean-logs" ]
then
echo "Cleaning *.log"
rm -rf *.log
exit 0
fi
echo "Usage: $0 [start|create|stop|restart|watch|tail|tailall|clean|clean-logs|call]"
echo "start -- Launch Redis Cluster instances."
echo "create [-f] -- Create a cluster using redis-cli --cluster create."
echo "stop -- Stop Redis Cluster instances."
echo "restart -- Restart Redis Cluster instances."
echo "watch -- Show CLUSTER NODES output (first 30 lines) of first node."
echo "tail <id> -- Run tail -f of instance at base port + ID."
echo "tailall -- Run tail -f for all the log files at once."
echo "clean -- Remove all instances data, logs, configs."
echo "clean-logs -- Remove just instances logs."
echo "call <cmd> -- Call a command (up to 7 arguments) on all nodes."

View File

@@ -247,7 +247,7 @@ export class DLOBSubscriberIO extends DLOBSubscriber {
l2Formatted['marketSlot'] !== lastMarketSlotAndTime.slot
) {
logger.warn(
`Updating market slot for ${marketArgs.marketName} with slot ${l2Formatted['marketSlot']}`
`Updating market slot for ${marketArgs.marketName} from ${lastMarketSlotAndTime.slot} -> ${l2Formatted['marketSlot']}`
);
this.lastMarketSlotMap
.get(marketArgs.marketType)
@@ -262,6 +262,13 @@ export class DLOBSubscriberIO extends DLOBSubscriber {
asks: l2Formatted.asks.slice(0, 100),
});
console.log(
`bbo: ${l2Formatted['marketName']}`,
(+l2Formatted.bids[0].price / 1e6).toFixed(4),
'//',
(+l2Formatted.asks[0].price / 1e6).toFixed(4)
);
this.redisClient.publish(
`${clientPrefix}orderbook_${marketType}_${marketArgs.marketIndex}`,
l2Formatted

View File

@@ -18,6 +18,8 @@ import {
PerpMarketConfig,
SpotMarketConfig,
PhoenixSubscriber,
MarketType,
OraclePriceData,
} from '@drift-labs/sdk';
import { RedisClient, RedisClientPrefix } from '@drift/common/clients';
@@ -25,6 +27,7 @@ import { logger, setLogLevel } from '../utils/logger';
import {
SubscriberLookup,
getOpenbookSubscriber,
l2WithBNToStrings,
parsePositiveIntArray,
sleep,
} from '../utils/utils';
@@ -40,7 +43,7 @@ import {
} from '../dlobProvider';
import FEATURE_FLAGS from '../utils/featureFlags';
import { GeyserOrderSubscriber } from '../grpc/OrderSubscriberGRPC';
import express from 'express';
import express, { Response, Request } from 'express';
import { handleHealthCheck } from '../core/metrics';
import { setGlobalDispatcher, Agent } from 'undici';
@@ -453,6 +456,50 @@ const main = async () => {
}
};
const handleDebug = async (req: Request, res: Response) => {
const marketIndex = +req.query.marketIndex;
let marketType: MarketType = MarketType.PERP;
let oraclePriceData: OraclePriceData;
if (req.query.marketType === 'spot') {
marketType = MarketType.SPOT;
oraclePriceData = driftClient.getOracleDataForSpotMarket(marketIndex);
} else {
oraclePriceData = driftClient.getOracleDataForPerpMarket(marketIndex);
}
try {
const slot = slotSource.getSlot();
const dlob = await dlobProvider.getDLOB(slot);
const l2 = dlob.getL2({
marketIndex,
marketType,
depth: 5,
slot,
oraclePriceData,
});
const l3 = dlob.getL3({
marketIndex,
marketType,
slot,
oraclePriceData,
});
const state = {
dlobSize: dlobProvider.size(),
slot,
markets: {
perp: perpMarketInfos,
spot: spotMarketInfos,
},
l2: l2WithBNToStrings(l2),
l3,
};
res.json(state);
} catch (e) {
res.status(500).json({ error: e.message });
}
};
app.get('/debug', handleDebug);
app.get(
'/health',
handleHealthCheck(WS_FALLBACK_FETCH_INTERVAL, dlobProvider)

View File

@@ -37,6 +37,21 @@ export const l2WithBNToStrings = (l2: L2OrderBook): any => {
return l2;
};
export const l3WithBNToStrings = (l3: L3OrderBook): any => {
for (const key of Object.keys(l3)) {
for (const idx in l3[key]) {
const level = l3[key][idx];
l3[key][idx] = {
price: level.price.toString(),
size: level.size.toString(),
maker: level.maker.toBase58(),
orderId: level.orderId.toString(),
};
}
}
return l3;
};
export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}

View File

@@ -460,11 +460,12 @@
kuler "^2.0.0"
"@drift-labs/sdk@file:./drift-common/protocol/sdk", "@drift-labs/sdk@file:drift-common/protocol/sdk":
version "2.96.0-beta.25"
version "2.103.0-beta.8"
dependencies:
"@coral-xyz/anchor" "0.28.0"
"@coral-xyz/anchor-30" "npm:@coral-xyz/anchor@0.30.1"
"@ellipsis-labs/phoenix-sdk" "^1.4.2"
"@grpc/grpc-js" "^1.8.0"
"@openbook-dex/openbook-v2" "0.2.10"
"@project-serum/serum" "^0.13.38"
"@pythnetwork/client" "2.5.3"
@@ -473,18 +474,20 @@
"@solana/spl-token" "0.3.7"
"@solana/web3.js" "1.92.3"
"@switchboard-xyz/on-demand" "1.2.42"
"@triton-one/yellowstone-grpc" "0.6.0"
anchor-bankrun "^0.3.0"
node-cache "^5.1.2"
rpc-websockets "7.5.1"
solana-bankrun "^0.3.0"
strict-event-emitter-types "^2.0.0"
tweetnacl "1.0.3"
uuid "^8.3.2"
zstddec "^0.1.0"
"@drift/common@file:./drift-common/common-ts":
version "1.0.0"
dependencies:
"@drift-labs/sdk" "file:../../Library/Caches/Yarn/v6/npm-@drift-common-1.0.0-79940387-1595-4bdc-b73c-2bd48440a03a-1729802406625/node_modules/@drift/protocol/sdk"
"@drift-labs/sdk" "file:../../Library/Caches/Yarn/v6/npm-@drift-common-1.0.0-2478b7b0-73c2-48c2-8cdc-077c34db7294-1733168269147/node_modules/@drift/protocol/sdk"
"@jest/globals" "^29.3.1"
"@slack/web-api" "^6.4.0"
"@solana/spl-token" "^0.3.8"
@@ -2140,6 +2143,13 @@
js-yaml "^4.1.0"
protobufjs "^7.2.6"
"@triton-one/yellowstone-grpc@0.6.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@triton-one/yellowstone-grpc/-/yellowstone-grpc-0.6.0.tgz#9e6376cec8a42284c23dc195df2c3423c87c4f27"
integrity sha512-rgdZM2N3U9/d/QKOI5PP+9rSHUl2oSI5Uwzvuss8y/mtTaHFjbOMpXpQXviIeDkusOa+mef4wLYrbjEZCwTXiw==
dependencies:
"@grpc/grpc-js" "^1.8.0"
"@triton-one/yellowstone-grpc@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@triton-one/yellowstone-grpc/-/yellowstone-grpc-0.3.0.tgz#2318b98b9ee80871e4ee8f9259f3d8673bf2c576"
@@ -6307,7 +6317,7 @@ tsutils@^3.21.0:
dependencies:
tslib "^1.8.1"
tweetnacl@^1.0.3:
tweetnacl@1.0.3, tweetnacl@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==