Skip to content

Commit

Permalink
Implement CachedQueryResolver
Browse files Browse the repository at this point in the history
  • Loading branch information
hectorgomezv committed Jul 19, 2024
1 parent db659f1 commit 6c5c83b
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 81 deletions.
2 changes: 2 additions & 0 deletions src/datasources/accounts/accounts.datasource.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AccountsDatasource } from '@/datasources/accounts/accounts.datasource';
import { FakeCacheService } from '@/datasources/cache/__tests__/fake.cache.service';
import { MAX_TTL } from '@/datasources/cache/constants';
import { CacheDir } from '@/datasources/cache/entities/cache-dir.entity';
import { CachedQueryResolver } from '@/datasources/db/cached-query-resolver';
import { PostgresDatabaseMigrator } from '@/datasources/db/postgres-database.migrator';
import { accountDataTypeBuilder } from '@/domain/accounts/entities/__tests__/account-data-type.builder';
import { upsertAccountDataSettingsDtoBuilder } from '@/domain/accounts/entities/__tests__/upsert-account-data-settings.dto.entity.builder';
Expand Down Expand Up @@ -43,6 +44,7 @@ describe('AccountsDatasource tests', () => {
target = new AccountsDatasource(
fakeCacheService,
sql,
new CachedQueryResolver(mockLoggingService, fakeCacheService),
mockLoggingService,
mockConfigurationService,
);
Expand Down
33 changes: 14 additions & 19 deletions src/datasources/accounts/accounts.datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ICacheService,
} from '@/datasources/cache/cache.service.interface';
import { MAX_TTL } from '@/datasources/cache/constants';
import { getFromCacheOrExecuteAndCache } from '@/datasources/db/utils';
import { CachedQueryResolver } from '@/datasources/db/cached-query-resolver';
import { AccountDataSetting } from '@/domain/accounts/entities/account-data-setting.entity';
import { AccountDataType } from '@/domain/accounts/entities/account-data-type.entity';
import { Account } from '@/domain/accounts/entities/account.entity';
Expand All @@ -29,6 +29,7 @@ export class AccountsDatasource implements IAccountsDatasource, OnModuleInit {
constructor(
@Inject(CacheService) private readonly cacheService: ICacheService,
@Inject('DB_INSTANCE') private readonly sql: postgres.Sql,
private readonly cachedQueryResolver: CachedQueryResolver,
@Inject(LoggingService) private readonly loggingService: ILoggingService,
@Inject(IConfigurationService)
private readonly configurationService: IConfigurationService,
Expand Down Expand Up @@ -70,15 +71,13 @@ export class AccountsDatasource implements IAccountsDatasource, OnModuleInit {

async getAccount(args: { address: `0x${string}` }): Promise<Account> {
const cacheDir = CacheRouter.getAccountCacheDir(args.address);
const [account] = await getFromCacheOrExecuteAndCache<Account[]>(
this.loggingService,
this.cacheService,
const [account] = await this.cachedQueryResolver.get<Account[]>({
cacheDir,
this.sql<
query: this.sql<
Account[]
>`SELECT * FROM accounts WHERE address = ${args.address}`,
this.defaultExpirationTimeInSeconds,
);
ttl: this.defaultExpirationTimeInSeconds,
});

if (!account) {
throw new NotFoundException('Error getting account.');
Expand Down Expand Up @@ -108,30 +107,26 @@ export class AccountsDatasource implements IAccountsDatasource, OnModuleInit {

async getDataTypes(): Promise<AccountDataType[]> {
const cacheDir = CacheRouter.getAccountDataTypesCacheDir();
return getFromCacheOrExecuteAndCache<AccountDataType[]>(
this.loggingService,
this.cacheService,
return this.cachedQueryResolver.get<AccountDataType[]>({
cacheDir,
this.sql<AccountDataType[]>`SELECT * FROM account_data_types`,
MAX_TTL,
);
query: this.sql<AccountDataType[]>`SELECT * FROM account_data_types`,
ttl: MAX_TTL,
});
}

async getAccountDataSettings(args: {
address: `0x${string}`;
}): Promise<AccountDataSetting[]> {
const account = await this.getAccount(args);
const cacheDir = CacheRouter.getAccountDataSettingsCacheDir(args.address);
return getFromCacheOrExecuteAndCache<AccountDataSetting[]>(
this.loggingService,
this.cacheService,
return this.cachedQueryResolver.get<AccountDataSetting[]>({
cacheDir,
this.sql<AccountDataSetting[]>`
query: this.sql<AccountDataSetting[]>`
SELECT ads.* FROM account_data_settings ads INNER JOIN account_data_types adt
ON ads.account_data_type_id = adt.id
WHERE ads.account_id = ${account.id} AND adt.is_active IS TRUE;`,
this.defaultExpirationTimeInSeconds,
);
ttl: this.defaultExpirationTimeInSeconds,
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { IConfigurationService } from '@/config/configuration.service.interface'
import { CounterfactualSafesDatasource } from '@/datasources/accounts/counterfactual-safes/counterfactual-safes.datasource';
import { FakeCacheService } from '@/datasources/cache/__tests__/fake.cache.service';
import { CacheDir } from '@/datasources/cache/entities/cache-dir.entity';
import { CachedQueryResolver } from '@/datasources/db/cached-query-resolver';
import { PostgresDatabaseMigrator } from '@/datasources/db/postgres-database.migrator';
import { createCounterfactualSafeDtoBuilder } from '@/domain/accounts/counterfactual-safes/entities/__tests__/create-counterfactual-safe.dto.entity.builder';
import { accountBuilder } from '@/domain/accounts/entities/__tests__/account.builder';
Expand Down Expand Up @@ -43,6 +44,7 @@ describe('CounterfactualSafesDatasource tests', () => {
target = new CounterfactualSafesDatasource(
fakeCacheService,
sql,
new CachedQueryResolver(mockLoggingService, fakeCacheService),
mockLoggingService,
mockConfigurationService,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
CacheService,
ICacheService,
} from '@/datasources/cache/cache.service.interface';
import { getFromCacheOrExecuteAndCache } from '@/datasources/db/utils';
import { CachedQueryResolver } from '@/datasources/db/cached-query-resolver';
import { CounterfactualSafe } from '@/domain/accounts/counterfactual-safes/entities/counterfactual-safe.entity';
import { CreateCounterfactualSafeDto } from '@/domain/accounts/counterfactual-safes/entities/create-counterfactual-safe.dto.entity';
import { Account } from '@/domain/accounts/entities/account.entity';
Expand All @@ -22,6 +22,7 @@ export class CounterfactualSafesDatasource
constructor(
@Inject(CacheService) private readonly cacheService: ICacheService,
@Inject('DB_INSTANCE') private readonly sql: postgres.Sql,
private readonly cachedQueryResolver: CachedQueryResolver,
@Inject(LoggingService) private readonly loggingService: ILoggingService,
@Inject(IConfigurationService)
private readonly configurationService: IConfigurationService,
Expand Down Expand Up @@ -58,16 +59,14 @@ export class CounterfactualSafesDatasource
args.chainId,
args.predictedAddress,
);
const [counterfactualSafe] = await getFromCacheOrExecuteAndCache<
const [counterfactualSafe] = await this.cachedQueryResolver.get<
CounterfactualSafe[]
>(
this.loggingService,
this.cacheService,
>({
cacheDir,
this.sql<CounterfactualSafe[]>`
query: this.sql<CounterfactualSafe[]>`
SELECT * FROM counterfactual_safes WHERE chain_id = ${args.chainId} AND predicted_address = ${args.predictedAddress}`,
this.defaultExpirationTimeInSeconds,
);
ttl: this.defaultExpirationTimeInSeconds,
});

if (!counterfactualSafe) {
throw new NotFoundException('Error getting Counterfactual Safe.');
Expand All @@ -82,14 +81,12 @@ export class CounterfactualSafesDatasource
const cacheDir = CacheRouter.getCounterfactualSafesCacheDir(
args.account.address,
);
return getFromCacheOrExecuteAndCache<CounterfactualSafe[]>(
this.loggingService,
this.cacheService,
return this.cachedQueryResolver.get<CounterfactualSafe[]>({
cacheDir,
this.sql<CounterfactualSafe[]>`
query: this.sql<CounterfactualSafe[]>`
SELECT * FROM counterfactual_safes WHERE account_id = ${args.account.id}`,
this.defaultExpirationTimeInSeconds,
);
ttl: this.defaultExpirationTimeInSeconds,
});
}

async deleteCounterfactualSafe(args: {
Expand Down
61 changes: 61 additions & 0 deletions src/datasources/db/cached-query-resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
CacheService,
ICacheService,
} from '@/datasources/cache/cache.service.interface';
import { CacheDir } from '@/datasources/cache/entities/cache-dir.entity';
import { ILoggingService, LoggingService } from '@/logging/logging.interface';
import { asError } from '@/logging/utils';
import {
Inject,
Injectable,
InternalServerErrorException,
} from '@nestjs/common';
import postgres from 'postgres';

@Injectable()
// TODO: add/implement interface
export class CachedQueryResolver {
constructor(
@Inject(LoggingService) private readonly loggingService: ILoggingService,
@Inject(CacheService) private readonly cacheService: ICacheService,
) {}

/**
* Returns the content from cache or executes the query and caches the result.
* If the specified {@link CacheDir} is empty, the query is executed and the result is cached.
* If the specified {@link CacheDir} is not empty, the pointed content is returned.
*
* @param cacheDir {@link CacheDir} to use for caching
* @param query query to execute
* @param ttl time to live for the cache
* @returns content from cache or query result
*/
async get<T extends postgres.MaybeRow[]>(args: {
cacheDir: CacheDir;
query: postgres.PendingQuery<T>;
ttl: number;
}): Promise<T> {
const { key, field } = args.cacheDir;
const cached = await this.cacheService.get(args.cacheDir);
if (cached != null) {
this.loggingService.debug({ type: 'cache_hit', key, field });
return JSON.parse(cached);
}
this.loggingService.debug({ type: 'cache_miss', key, field });

// log & hide database errors
const result = await args.query.catch((e) => {
this.loggingService.error(asError(e).message);
throw new InternalServerErrorException();
});

if (result.count > 0) {
await this.cacheService.set(
args.cacheDir,
JSON.stringify(result),
args.ttl,
);
}
return result;
}
}
48 changes: 0 additions & 48 deletions src/datasources/db/utils.ts

This file was deleted.

0 comments on commit 6c5c83b

Please sign in to comment.