Skip to content

Commit 6206bd1

Browse files
committed
Merge branch 'develop' into feature/closeable
# Conflicts: # packages/sequencer/src/storage/Database.ts
2 parents 8ec5fcb + 13f9bed commit 6206bd1

30 files changed

+485
-168
lines changed

.github/workflows/pull-request-develop.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ jobs:
9191

9292
- name: "Test"
9393
run: npm run test:ci
94+
env:
95+
IN_CI: true
9496

9597
integration:
9698
runs-on: ubuntu-latest

packages/common/src/log.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ function logProvable(
2525
}
2626
/* eslint-enable */
2727

28+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
29+
if (process.env?.IN_CI ?? false) {
30+
loglevel.setLevel("ERROR");
31+
}
32+
2833
const timeMap: Record<string, number> = {};
2934

3035
function time(label = "time") {
@@ -111,7 +116,10 @@ export const log = {
111116
},
112117

113118
setLevel: (level: LogLevelDesc) => {
114-
loglevel.setLevel(level);
119+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
120+
if (!(process.env?.IN_CI ?? false)) {
121+
loglevel.setLevel(level);
122+
}
115123
},
116124

117125
get levels() {

packages/common/src/utils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export function requireTrue(
1919
}
2020
}
2121

22+
/**
23+
* Utility function to split an array of type T into a record <K, T[]> based on a
24+
* function T => K that determines the key of each record
25+
*/
2226
export function splitArray<T, K extends string | number>(
2327
arr: T[],
2428
split: (t: T) => K

packages/persistance/src/PrismaDatabaseConnection.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,8 @@ export class PrismaDatabaseConnection
137137
public async close() {
138138
await this.prismaClient.$disconnect();
139139
}
140+
141+
public async executeInTransaction(f: () => Promise<void>) {
142+
await this.prismaClient.$transaction(f);
143+
}
140144
}

packages/persistance/src/PrismaRedisDatabase.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
RedisConnection,
1919
RedisConnectionConfig,
2020
RedisConnectionModule,
21+
RedisTransaction,
2122
} from "./RedisConnection";
2223

2324
export interface PrismaRedisCombinedConfig {
@@ -49,6 +50,10 @@ export class PrismaRedisDatabase
4950
return this.redis.redisClient;
5051
}
5152

53+
public get currentMulti(): RedisTransaction {
54+
return this.redis.currentMulti;
55+
}
56+
5257
public create(childContainerProvider: ChildContainerProvider) {
5358
super.create(childContainerProvider);
5459
this.prisma.create(childContainerProvider);
@@ -79,4 +84,12 @@ export class PrismaRedisDatabase
7984
await this.prisma.pruneDatabase();
8085
await this.redis.pruneDatabase();
8186
}
87+
88+
public async executeInTransaction(f: () => Promise<void>) {
89+
// TODO Long-term we want to somehow make sure we can rollback one data source
90+
// if commiting the other one's transaction fails
91+
await this.prisma.executeInTransaction(async () => {
92+
await this.redis.executeInTransaction(f);
93+
});
94+
}
8295
}

packages/persistance/src/RedisConnection.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ export interface RedisConnectionConfig {
1414
username?: string;
1515
}
1616

17+
export type RedisTransaction = ReturnType<RedisClientType["multi"]>;
18+
1719
export interface RedisConnection {
1820
get redisClient(): RedisClientType;
21+
get currentMulti(): RedisTransaction;
1922
}
2023

2124
export class RedisConnectionModule
@@ -82,4 +85,20 @@ export class RedisConnectionModule
8285
public async pruneDatabase() {
8386
await this.redisClient.flushDb();
8487
}
88+
89+
private multi?: RedisTransaction;
90+
91+
public get currentMulti() {
92+
if (this.multi === undefined) {
93+
throw new Error("Redis multi was access outside of a transaction");
94+
}
95+
return this.multi;
96+
}
97+
98+
public async executeInTransaction(f: () => Promise<void>) {
99+
this.multi = this.redisClient.multi();
100+
await f();
101+
await this.multi.exec();
102+
this.multi = undefined;
103+
}
85104
}

packages/persistance/src/services/prisma/PrismaBlockStorage.ts

Lines changed: 58 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
BlockStorage,
99
BlockWithResult,
1010
BlockWithPreviousResult,
11+
BlockWithMaybeResult,
1112
} from "@proto-kit/sequencer";
1213
import { filterNonNull, log } from "@proto-kit/common";
1314
import {
@@ -39,7 +40,7 @@ export class PrismaBlockStorage
3940

4041
private async getBlockByQuery(
4142
where: { height: number } | { hash: string }
42-
): Promise<BlockWithResult | undefined> {
43+
): Promise<BlockWithMaybeResult | undefined> {
4344
const dbResult = await this.connection.prismaClient.block.findFirst({
4445
where,
4546
include: {
@@ -57,18 +58,15 @@ export class PrismaBlockStorage
5758
const transactions = dbResult.transactions.map<TransactionExecutionResult>(
5859
(txresult) => this.transactionResultMapper.mapIn([txresult, txresult.tx])
5960
);
60-
if (dbResult.result === undefined || dbResult.result === null) {
61-
throw new Error(
62-
`No Metadata has been set for block ${JSON.stringify(where)} yet`
63-
);
64-
}
6561

6662
return {
6763
block: {
6864
...this.blockMapper.mapIn(dbResult),
6965
transactions,
7066
},
71-
result: this.blockResultMapper.mapIn(dbResult.result),
67+
result: dbResult.result
68+
? this.blockResultMapper.mapIn(dbResult.result)
69+
: undefined,
7270
};
7371
}
7472

@@ -100,45 +98,42 @@ export class PrismaBlockStorage
10098

10199
const { prismaClient } = this.connection;
102100

103-
await prismaClient.$transaction([
104-
prismaClient.transaction.createMany({
105-
data: block.transactions.map((txr) =>
106-
this.transactionMapper.mapOut(txr.tx)
107-
),
108-
skipDuplicates: true,
109-
}),
110-
111-
prismaClient.block.create({
112-
data: {
113-
...encodedBlock,
114-
beforeNetworkState:
115-
encodedBlock.beforeNetworkState as Prisma.InputJsonObject,
116-
duringNetworkState:
117-
encodedBlock.duringNetworkState as Prisma.InputJsonObject,
118-
119-
transactions: {
120-
createMany: {
121-
data: transactions.map((tx) => {
122-
return {
123-
status: tx.status,
124-
statusMessage: tx.statusMessage,
125-
txHash: tx.txHash,
126-
127-
stateTransitions:
128-
tx.stateTransitions as Prisma.InputJsonArray,
129-
protocolTransitions:
130-
tx.protocolTransitions as Prisma.InputJsonArray,
131-
events: tx.events as Prisma.InputJsonArray,
132-
};
133-
}),
134-
skipDuplicates: true,
135-
},
136-
},
101+
await prismaClient.transaction.createMany({
102+
data: block.transactions.map((txr) =>
103+
this.transactionMapper.mapOut(txr.tx)
104+
),
105+
skipDuplicates: true,
106+
});
107+
108+
await prismaClient.block.create({
109+
data: {
110+
...encodedBlock,
111+
beforeNetworkState:
112+
encodedBlock.beforeNetworkState as Prisma.InputJsonObject,
113+
duringNetworkState:
114+
encodedBlock.duringNetworkState as Prisma.InputJsonObject,
137115

138-
batchHeight: undefined,
116+
transactions: {
117+
createMany: {
118+
data: transactions.map((tx) => {
119+
return {
120+
status: tx.status,
121+
statusMessage: tx.statusMessage,
122+
txHash: tx.txHash,
123+
124+
stateTransitions: tx.stateTransitions as Prisma.InputJsonArray,
125+
protocolTransitions:
126+
tx.protocolTransitions as Prisma.InputJsonArray,
127+
events: tx.events as Prisma.InputJsonArray,
128+
};
129+
}),
130+
skipDuplicates: true,
131+
},
139132
},
140-
}),
141-
]);
133+
134+
batchHeight: undefined,
135+
},
136+
});
142137
}
143138

144139
public async pushResult(result: BlockResult): Promise<void> {
@@ -169,7 +164,9 @@ export class PrismaBlockStorage
169164
return (result?._max.height ?? -1) + 1;
170165
}
171166

172-
public async getLatestBlock(): Promise<BlockWithResult | undefined> {
167+
public async getLatestBlockAndResult(): Promise<
168+
BlockWithMaybeResult | undefined
169+
> {
173170
const latestBlock = await this.connection.prismaClient.$queryRaw<
174171
{ hash: string }[]
175172
>`SELECT b1."hash" FROM "Block" b1
@@ -185,6 +182,22 @@ export class PrismaBlockStorage
185182
});
186183
}
187184

185+
public async getLatestBlock(): Promise<BlockWithResult | undefined> {
186+
const result = await this.getLatestBlockAndResult();
187+
if (result !== undefined) {
188+
if (result.result === undefined) {
189+
throw new Error(
190+
`Block result for block ${result.block.height.toString()} not found`
191+
);
192+
}
193+
return {
194+
block: result.block,
195+
result: result.result,
196+
};
197+
}
198+
return result;
199+
}
200+
188201
public async getNewBlocks(): Promise<BlockWithPreviousResult[]> {
189202
const blocks = await this.connection.prismaClient.block.findMany({
190203
where: {

packages/persistance/src/services/prisma/PrismaMessageStorage.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,25 +51,24 @@ export class PrismaMessageStorage implements MessageStorage {
5151
);
5252

5353
const { prismaClient } = this.connection;
54-
await prismaClient.$transaction([
55-
prismaClient.transaction.createMany({
56-
data: transactions,
57-
skipDuplicates: true,
58-
}),
5954

60-
prismaClient.incomingMessageBatch.create({
61-
data: {
62-
fromMessageHash,
63-
toMessageHash,
64-
messages: {
65-
createMany: {
66-
data: transactions.map((transaction) => ({
67-
transactionHash: transaction.hash,
68-
})),
69-
},
55+
await prismaClient.transaction.createMany({
56+
data: transactions,
57+
skipDuplicates: true,
58+
});
59+
60+
await prismaClient.incomingMessageBatch.create({
61+
data: {
62+
fromMessageHash,
63+
toMessageHash,
64+
messages: {
65+
createMany: {
66+
data: transactions.map((transaction) => ({
67+
transactionHash: transaction.hash,
68+
})),
7069
},
7170
},
72-
}),
73-
]);
71+
},
72+
});
7473
}
7574
}

packages/persistance/src/services/prisma/PrismaStateService.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,17 @@ export class PrismaStateService implements AsyncStateService {
3636
mask: this.mask,
3737
}));
3838

39-
await prismaClient.$transaction([
40-
prismaClient.state.deleteMany({
41-
where: {
42-
path: {
43-
in: this.cache.map((x) => new Decimal(x.key.toString())),
44-
},
45-
mask: this.mask,
39+
await prismaClient.state.deleteMany({
40+
where: {
41+
path: {
42+
in: this.cache.map((x) => new Decimal(x.key.toString())),
4643
},
47-
}),
48-
prismaClient.state.createMany({
49-
data,
50-
}),
51-
]);
44+
mask: this.mask,
45+
},
46+
});
47+
await prismaClient.state.createMany({
48+
data,
49+
});
5250

5351
this.cache = [];
5452
}

packages/persistance/src/services/redis/RedisMerkleTreeStore.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class RedisMerkleTreeStore implements AsyncMerkleTreeStore {
3434
}
3535

3636
try {
37-
await this.connection.redisClient.mSet(array.flat(1));
37+
this.connection.currentMulti.mSet(array.flat(1));
3838
} catch (error) {
3939
log.error(error);
4040
}
@@ -62,8 +62,8 @@ export class RedisMerkleTreeStore implements AsyncMerkleTreeStore {
6262
public writeNodes(nodes: MerkleTreeNode[]): void {
6363
this.cache = this.cache.concat(nodes);
6464
// TODO Filter distinct
65-
// We might not even need this, since the distinctness filter might already
66-
// be implicitely done by the layer above (i.e. cachedmtstore)
65+
// We might not even need this, since the distinctness filter might already
66+
// be implicitely done by the layer above (i.e. cachedmtstore)
6767

6868
// Leaving this for now until I get to implementing it
6969
// const concat = this.cache.concat(nodes);

0 commit comments

Comments
 (0)