Skip to content

Commit

Permalink
refactor(experimental): a cache for RPC GraphQL (#1661)
Browse files Browse the repository at this point in the history
  • Loading branch information
buffalojoec authored Oct 9, 2023
1 parent 5586a1b commit 278784a
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 2 deletions.
63 changes: 63 additions & 0 deletions packages/rpc-graphql/src/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
export interface GraphQLCache {
flush(): void;
get(key: string | bigint, variables: unknown): unknown | null;
insert(key: string | bigint, variables: unknown, value: unknown): void;
}

// Basic in-memory cache for Node.js
const inMemoryCache: { [key: string]: string } = {};

const stringifyValue = (value: unknown) =>
JSON.stringify(value, (_, value) => {
if (typeof value === 'bigint') {
return value.toString() + 'n';
}
return value;
});

const parseValue = (value: string) =>
JSON.parse(value, (_, value) => {
if (typeof value === 'string' && /\d+n$/.test(value)) {
return BigInt(value.slice(0, -1));
}
return value;
});

const cacheKey = (key: string | bigint, variables: unknown): string =>
`GraphQLCache:${stringifyValue(key)}:${stringifyValue(variables)}`;

export function createGraphQLCache(): GraphQLCache {
return __BROWSER__
? {
// Browser
flush: () => {
// Clear all entries from the localStorage related to this cache
for (let i = localStorage.length - 1; i >= 0; i--) {
const storageKey = localStorage.key(i);
if (storageKey && storageKey.startsWith('GraphQLCache:')) {
localStorage.removeItem(storageKey);
}
}
},
get: (key, variables) => {
const value = localStorage.getItem(cacheKey(key, variables));
return value === null ? null : parseValue(value);
},
insert: (key, variables, value) => {
localStorage.setItem(cacheKey(key, variables), stringifyValue(value));
},
}
: {
// Node.js
flush: () => {
Object.keys(inMemoryCache).forEach(key => delete inMemoryCache[key]);
},
get: (key, variables) => {
const value = inMemoryCache[cacheKey(key, variables)];
return value === undefined ? null : parseValue(value);
},
insert: (key, variables, value) => {
inMemoryCache[cacheKey(key, variables)] = stringifyValue(value);
},
};
}
6 changes: 5 additions & 1 deletion packages/rpc-graphql/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { SolanaRpcMethods } from '@solana/rpc-core';
import { Rpc } from '@solana/rpc-transport/dist/types/json-rpc-types';

import { createGraphQLCache, GraphQLCache } from './cache';

export interface RpcGraphQLContext {
cache: GraphQLCache;
rpc: Rpc<SolanaRpcMethods>;
}

export function createSolanaGraphQLContext(rpc: Rpc<SolanaRpcMethods>): RpcGraphQLContext {
return { rpc };
const cache = createGraphQLCache();
return { cache, rpc };
}
4 changes: 3 additions & 1 deletion packages/rpc-graphql/src/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ export function createRpcGraphQL(rpc: Rpc<SolanaRpcMethods>): RpcGraphQL {
return {
context,
async query(source: string | Source, variableValues?: { readonly [variable: string]: unknown }) {
return graphql({
const result = await graphql({
contextValue: this.context,
schema: this.schema,
source,
variableValues,
});
this.context.cache.flush();
return result;
},
schema,
};
Expand Down

0 comments on commit 278784a

Please sign in to comment.