Skip to content

Commit

Permalink
[OTE-753] add new persistent cache table (#2175)
Browse files Browse the repository at this point in the history
  • Loading branch information
jerryfan01234 authored Aug 30, 2024
1 parent 61fa593 commit abfec2a
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 0 deletions.
13 changes: 13 additions & 0 deletions indexer/packages/postgres/__tests__/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import {
TransactionCreateObject,
TransferCreateObject,
WalletCreateObject,
PersistentCacheCreateObject,
} from '../../src/types';
import { denomToHumanReadableConversion } from './conversion-helpers';

Expand Down Expand Up @@ -940,3 +941,15 @@ export const defaultLeaderboardPnlOneDayToUpsert: LeaderboardPnlCreateObject = {
currentEquity: '1000',
rank: 1,
};

// ============== Persistent cache Data ==============

export const defaultKV: PersistentCacheCreateObject = {
key: 'someKey',
value: 'someValue',
};

export const defaultKV2: PersistentCacheCreateObject = {
key: 'otherKey',
value: 'otherValue',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { PersistentCacheFromDatabase } from '../../src/types';
import { clearData, migrate, teardown } from '../../src/helpers/db-helpers';
import { defaultKV, defaultKV2 } from '../helpers/constants';
import * as PersistentCacheTable from '../../src/stores/persistent-cache-table';

describe('Persistent cache store', () => {
beforeAll(async () => {
await migrate();
});

afterEach(async () => {
await clearData();
});

afterAll(async () => {
await teardown();
});

it('Successfully creates a key value pair', async () => {
await PersistentCacheTable.create(defaultKV);
});

it('Successfully upserts a kv pair multiple times', async () => {
const newKv = {
...defaultKV,
value: 'someOtherValue',
};
await PersistentCacheTable.upsert(newKv);
let kv: PersistentCacheFromDatabase | undefined = await PersistentCacheTable.findById(
defaultKV.key,
);
expect(kv).toEqual(expect.objectContaining(newKv));

const newKv2 = {
...defaultKV,
value: 'someOtherValue2',
};
await PersistentCacheTable.upsert(newKv2);
kv = await PersistentCacheTable.findById(defaultKV.key);

expect(kv).toEqual(expect.objectContaining(newKv2));
});

it('Successfully finds all kv pairs', async () => {
await Promise.all([
PersistentCacheTable.create(defaultKV),
PersistentCacheTable.create(defaultKV2),
]);

const kvs: PersistentCacheFromDatabase[] = await PersistentCacheTable.findAll(
{},
[],
{ readReplica: true },
);

expect(kvs.length).toEqual(2);
expect(kvs).toEqual(expect.arrayContaining([
expect.objectContaining(defaultKV),
expect.objectContaining(defaultKV2),
]));
});

it('Successfully finds a kv pair', async () => {
await PersistentCacheTable.create(defaultKV);

const kv: PersistentCacheFromDatabase | undefined = await PersistentCacheTable.findById(
defaultKV.key,
);

expect(kv).toEqual(expect.objectContaining(defaultKV));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as Knex from 'knex';

export async function up(knex: Knex): Promise<void> {
return knex.schema.createTable('persistent_cache', (table) => {
table.string('key').primary().notNullable();
table.string('value').notNullable();
});
}

export async function down(knex: Knex): Promise<void> {
return knex.schema.dropTable('persistent_cache');
}
1 change: 1 addition & 0 deletions indexer/packages/postgres/src/helpers/db-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const layer1Tables = [
'trading_rewards',
'trading_reward_aggregations',
'compliance_status',
'persistent_cache',
];

/**
Expand Down
2 changes: 2 additions & 0 deletions indexer/packages/postgres/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export { default as TradingRewardModel } from './models/trading-reward-model';
export { default as TradingRewardAggregationModel } from './models/trading-reward-aggregation-model';
export { default as SubaccountUsernamesModel } from './models/subaccount-usernames-model';
export { default as LeaderboardPnlModel } from './models/leaderboard-pnl-model';
export { default as PersistentCacheModel } from './models/persistent-cache-model';

export * as AssetTable from './stores/asset-table';
export * as AssetPositionTable from './stores/asset-position-table';
Expand All @@ -43,6 +44,7 @@ export * as TradingRewardTable from './stores/trading-reward-table';
export * as TradingRewardAggregationTable from './stores/trading-reward-aggregation-table';
export * as LeaderboardPnlTable from './stores/leaderboard-pnl-table';
export * as SubaccountUsernamesTable from './stores/subaccount-usernames-table';
export * as PersistentCacheTable from './stores/persistent-cache-table';

export * as perpetualMarketRefresher from './loops/perpetual-market-refresher';
export * as assetRefresher from './loops/asset-refresher';
Expand Down
31 changes: 31 additions & 0 deletions indexer/packages/postgres/src/models/persistent-cache-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import UpsertQueryBuilder from '../query-builders/upsert';
import BaseModel from './base-model';

export default class PersistentCacheModel extends BaseModel {
static get tableName() {
return 'persistent_cache';
}

static get idColumn() {
return 'key';
}

static relationMappings = {};

static get jsonSchema() {
return {
type: 'object',
required: ['key', 'value'],
properties: {
key: { type: 'string' },
value: { type: 'string' },
},
};
}

QueryBuilderType!: UpsertQueryBuilder<this>;

key!: string;

value!: string;
}
94 changes: 94 additions & 0 deletions indexer/packages/postgres/src/stores/persistent-cache-table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { QueryBuilder } from 'objection';

import { DEFAULT_POSTGRES_OPTIONS } from '../constants';
import { setupBaseQuery, verifyAllRequiredFields } from '../helpers/stores-helpers';
import Transaction from '../helpers/transaction';
import PersistentCacheModel from '../models/persistent-cache-model';
import {
Options,
Ordering,
QueryableField,
QueryConfig,
PersistentCacheColumns,
PersistentCacheCreateObject,
PersistentCacheFromDatabase,
PersistentCacheQueryConfig,
} from '../types';

export async function findAll(
{
key,
limit,
}: PersistentCacheQueryConfig,
requiredFields: QueryableField[],
options: Options = DEFAULT_POSTGRES_OPTIONS,
): Promise<PersistentCacheFromDatabase[]> {
verifyAllRequiredFields(
{
key,
limit,
} as QueryConfig,
requiredFields,
);

let baseQuery: QueryBuilder<PersistentCacheModel> = setupBaseQuery<PersistentCacheModel>(
PersistentCacheModel,
options,
);

if (key) {
baseQuery = baseQuery.where(PersistentCacheColumns.key, key);
}

if (options.orderBy !== undefined) {
for (const [column, order] of options.orderBy) {
baseQuery = baseQuery.orderBy(
column,
order,
);
}
} else {
baseQuery = baseQuery.orderBy(
PersistentCacheColumns.key,
Ordering.ASC,
);
}

if (limit) {
baseQuery = baseQuery.limit(limit);
}

return baseQuery.returning('*');
}

export async function create(
kvToCreate: PersistentCacheCreateObject,
options: Options = { txId: undefined },
): Promise<PersistentCacheFromDatabase> {
return PersistentCacheModel.query(
Transaction.get(options.txId),
).insert(kvToCreate).returning('*');
}

export async function upsert(
kvToUpsert: PersistentCacheCreateObject,
options: Options = { txId: undefined },
): Promise<PersistentCacheFromDatabase> {
const kvs: PersistentCacheModel[] = await PersistentCacheModel.query(
Transaction.get(options.txId),
).upsert(kvToUpsert).returning('*');
// should only ever be one key value pair
return kvs[0];
}
export async function findById(
kv: string,
options: Options = DEFAULT_POSTGRES_OPTIONS,
): Promise<PersistentCacheFromDatabase | undefined> {
const baseQuery: QueryBuilder<PersistentCacheModel> = setupBaseQuery<PersistentCacheModel>(
PersistentCacheModel,
options,
);
return baseQuery
.findById(kv)
.returning('*');
}
5 changes: 5 additions & 0 deletions indexer/packages/postgres/src/types/db-model-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,11 @@ export interface LeaderboardPnlFromDatabase {
rank: number,
}

export interface PersistentCacheFromDatabase {
key: string,
value: string,
}

export type SubaccountAssetNetTransferMap = { [subaccountId: string]:
{ [assetId: string]: string }, };
export type SubaccountToPerpetualPositionsMap = { [subaccountId: string]:
Expand Down
1 change: 1 addition & 0 deletions indexer/packages/postgres/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ export * from './trading-reward-aggregation-types';
export * from './pagination-types';
export * from './subaccount-usernames-types';
export * from './leaderboard-pnl-types';
export * from './persistent-cache-types';
export { PositionSide } from './position-types';
9 changes: 9 additions & 0 deletions indexer/packages/postgres/src/types/persistent-cache-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface PersistentCacheCreateObject {
key: string,
value: string,
}

export enum PersistentCacheColumns {
key = 'key',
value = 'value',
}
5 changes: 5 additions & 0 deletions indexer/packages/postgres/src/types/query-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export enum QueryableField {
USERNAME = 'username',
TIMESPAN = 'timeSpan',
RANK = 'rank',
KEY = 'key',
IS_WHITELIST_AFFILIATE = 'isWhitelistAffiliate',
}

Expand Down Expand Up @@ -323,3 +324,7 @@ export interface LeaderboardPnlQueryConfig extends QueryConfig {
[QueryableField.TIMESPAN]?: string[],
[QueryableField.RANK]?: number[],
}

export interface PersistentCacheQueryConfig extends QueryConfig {
[QueryableField.KEY]?: string,
}

0 comments on commit abfec2a

Please sign in to comment.