Caching
Godspeed provides caching of the tasks using redis/mem-cache or any other custom cache. You can cache the result of any task in the workflows.
Godspeed provides two pre-defined types of cache i.e. redis and in-memory. It allows using multiple caches in a single service with a default cache. Cache datasource should implement abstract class GSCacheAsDatasource
code is below. You can make multiple caches using this abstract class.
You can read and return from cache if data is present there, before executing any task where caching instruction is set.
The data is stored in form of GSStatus of the executed task. Both success and failure statuses can be stored and loaded.
Configuration
Add pre-defined cache in Godspeed
You can use Godspeed CLI to browse and install redis/mem-cache, maintained by Godspeed.
godspeed plugin add
,_, ╔════════════════════════════════════╗
(o,o) ║ Welcome to Godspeed ║
({___}) ║ World's First Meta Framework ║
" " ╚════════════════════════════════════╝
? Please select godspeed plugin to install: (Press <space> to select, <Up and Down> to move rows)
┌──────┬────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────┐
│ │ Name │ Description │
├──────┼────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ ◯ │ prisma-as-datastore │ Prisma as a datasource plugin for Godspeed Framework. │
├──────┼────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ ◯ │ aws-as-datasource │ aws as datasource plugin for Godspeed Framework │
├──────┼────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ ❯◯ │ redis-as-datasource │ redis as datasource plugin for Godspeed Framework │
├──────┼────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ ◯ │ graphql-as-eventsource │ graphql as eventsource plugin for Godspeed Framework │
├──────┼────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ ◯ │ mem-cache-as-datasource │ mem-cache as datasource plugin for Godspeed Fraework Framework │
└──────┴────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────┘
Alternatively, you can specify redis-as-datasource
or mem-cache-as-datasource
to add cache in your project directly.
godspeed plugin add <plugin-name>
Once you add the desired cache, you can find its code in src/types/redis.ts
where you can reuse our existing plugins or have code of your own plugin.
You can find its configuration in the src/datasources
e.g. redis.yaml
. All the redis database related configuration need to be set in this file. The redis.yaml
is an instance of type: redis
. You can have multiple instances of type: redis
called redis1.yaml
, redis2.yaml
etc.
├── api.yaml
├── redis.yaml
└── types
├── axios.ts
└── redis.ts
Default cache
Define default cache datasource in static configuration in caching
key.
log_level: debug
defaults:
lang: coffee
server_url: https://api.example.com:8443/v1/api
caching: <redis or mem-cache>
How to use caching in your tasks
Using caching in Typescript/Javascript tasks
Currently caching support is not provided when you call a datasource or function/workflow from typescript code. Check this github issue for more information.
For now if you want to use cache datasource within typescript code, you need to call it like any other datasource from within a typescript code. You need to manage caching and invalidations in your code. This requires some boilerplate till the above issue is implemented.
export default async function (ctx: GSContext, args: any) {
const redis_client = ctx.datasources['redis'].client;
let res: GSStatus;
const key = 'helloworld2';
try {
const cached_value = await redis_client.get(key);
if (!cached_value) {
res = await ctx.functions['com.gs.helloworld2'](ctx, {nice: "name", ...args});
await redis_client.set(key, JSON.stringify(res));
} else {
return JSON.parse(cached_value);
}
} catch(ex) {
return new GSStatus(false, 500, undefined, {message: "Internal Server Error", info: ex.message});
}
return res;
}
How to write your own cache plugin
You need to implement abstract class GSCacheAsDatasource
interface to write your own cache plugin.
export abstract class GSCachingDataSource extends GSDataSource {
//Redis options are available [here](https://redis.io/commands/set/) Client may or may not support all actions. RedisOptions is a superset based on what Redis supports
public abstract set(key:string, val: any, options: RedisOptions): any;
public abstract get(key: string): any; //Return the value stored against the key
public abstract del(key: string): any; //Delete the key
}
export type RedisOptions = {
EX? : number,
PX? : number,
EXAT?: number,
NX?: boolean,
XX?: boolean,
KEEPTTL?: boolean,
GET?: boolean
}
Sample caching datasource implementation
This code is sourced from mem-cache plugin. You can use this as a reference to make or customise your own caching implementations.
mem-cache datasource plugin
Project structure
.
├── src
│ ├── datasources
│ │ ├── api.yaml
│ │ ├── mem-cache.yaml
│ │ └── types
│ │ ├── axios.ts
│ │ ├── mem-cache.ts
mem-cache config ( src/datasources/mem-cache.yaml )
type: mem-cache
initializing client and execution ( src/datasources/types/mem-cache.ts ):
import { GSContext, GSCachingDataSource, PlainObject, logger } from "@godspeedsystems/core";
export default class DataSource extends GSCachingDataSource {
protected async initClient(): Promise<PlainObject> {
this.client = {};
return this.client;
}
set(key: string, val: any, options: { EX?: number | undefined; PX?: number | undefined; EXAT?: number | undefined; NX?: boolean | undefined; XX?: boolean | undefined; KEEPTTL?: boolean | undefined; GET?: boolean | undefined; }) {
logger.debug('set key %s %o', key, this.client);
this.client[key] = val;
}
get(key: string) {
return this.client[key];
}
del(key: string) {
delete this.client[key];
}
execute(ctx: GSContext): Promise<any> {
throw new Error("Method not implemented.");
}
}