forked from inflection-zone/rean-admin
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #99 from REAN-Foundation/performance_fixes
Performance fixes
- Loading branch information
Showing
21 changed files
with
477 additions
and
449 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
|
||
export interface ICache { | ||
set(key: string, value: unknown): Promise<void>; | ||
get(key: string): Promise<unknown | undefined>; | ||
has(key: string): Promise<boolean>; | ||
delete(key: string): Promise<boolean>; | ||
clear(): Promise<void>; | ||
findAndClear(searchPattern: string): Promise<string[]>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
|
||
export class CacheMap<V> { | ||
|
||
private cache: Map<string, V>; | ||
|
||
constructor() { | ||
this.cache = new Map<string, V>(); | ||
} | ||
|
||
set(key: string, value: V): void { | ||
this.cache.set(key, value); | ||
} | ||
|
||
get(key: string): V | undefined { | ||
return this.cache.get(key); | ||
} | ||
|
||
has(key: string): boolean { | ||
return this.cache.has(key); | ||
} | ||
|
||
delete(key: string): boolean { | ||
return this.cache.delete(key); | ||
} | ||
|
||
clear(): void { | ||
this.cache.clear(); | ||
} | ||
|
||
findAndClear(searchPattern: string): string[] { | ||
let keys: string[] = []; | ||
for (let key of this.cache.keys()) { | ||
if (key.includes(searchPattern)) { | ||
keys.push(key); | ||
} | ||
} | ||
for (let key of keys) { | ||
this.cache.delete(key); | ||
} | ||
return keys; | ||
} | ||
|
||
} | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////// |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Cache | ||
|
||
## Setup KeyDB as a Redis cache | ||
KeyDB is a high-performance fork of Redis with a focus on multithreading and memory efficiency | ||
It is designed to be a drop-in replacement for Redis | ||
KeyDB is fully compatible with Redis and supports all Redis commands | ||
|
||
``` | ||
docker run \ | ||
-d --name keydb \ | ||
-p 6379:6379 \ | ||
-e "CACHE_PASSWORD=your-password" \ | ||
-v /path/to/your/data:/data \ | ||
-v /path/to/your/logs:/logs keydb/keydb \ | ||
eqalpha/keydb | ||
``` | ||
|
||
Process to connect with KeyDB is same as Redis. | ||
1. Run the docker container. | ||
2. Set the password by logging into container | ||
a. First run redis-cli as | ||
```# redis-cli``` | ||
b. Set the password using | ||
```# auth <your-password>``` | ||
3. Create a client and connect to KeyDB. | ||
4. Use the client to perform operations. | ||
5. Close the connection when done. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { InMemoryCache } from './inmemory.cache' | ||
import { RedisCache } from './redis.cache'; | ||
import type { ICache } from './cache.interface'; | ||
import { CACHE_TYPE } from '$env/static/private'; | ||
import { building } from '$app/environment'; | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
const getCache = () => { | ||
//code should not be executed during the build step. | ||
if (!building) { | ||
if (CACHE_TYPE === 'in-memory') { | ||
return new InMemoryCache(); | ||
} | ||
return new RedisCache(); | ||
} | ||
}; | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
export class CacheService { | ||
|
||
static _cache: ICache = getCache(); | ||
|
||
static get = async (key: string): Promise<any | undefined> => { | ||
console.log('CacheService.get', key); | ||
return CacheService._cache.get(key); | ||
} | ||
|
||
static set = async (key: string, value: any): Promise<void> => { | ||
await CacheService._cache.set(key, value); | ||
} | ||
|
||
static has = async (key: string): Promise<boolean> => { | ||
return CacheService._cache.has(key); | ||
} | ||
|
||
static delete = async (key: string): Promise<boolean> => { | ||
return CacheService._cache.delete(key); | ||
} | ||
|
||
static deleteMany = async (keys: string[]): Promise<boolean> => { | ||
let result = true; | ||
for (let key of keys) { | ||
result = result && await CacheService._cache.delete(key); | ||
} | ||
return result; | ||
} | ||
|
||
static findAndClear = async (searchPatterns: string[]): Promise<string[]> => { | ||
var keys: string[] = []; | ||
for (var substr of searchPatterns) | ||
{ | ||
var removedKeys = await CacheService._cache.findAndClear(substr); | ||
keys.push(...removedKeys); | ||
} | ||
return keys; | ||
} | ||
|
||
static clear = async (): Promise<void> => { | ||
await CacheService._cache.clear(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { CacheMap } from "./cache.map"; | ||
import { type ICache } from "./cache.interface"; | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
export class InMemoryCache implements ICache { | ||
|
||
private cache: CacheMap<any> = new CacheMap<any>(); | ||
|
||
constructor() { | ||
this.cache = new CacheMap<any>(); | ||
} | ||
|
||
set = async (key: string, value: any): Promise<void> => { | ||
this.cache.set(key, value); | ||
}; | ||
|
||
get = async (key: string): Promise<any | undefined> => { | ||
return this.cache.get(key); | ||
}; | ||
|
||
has = async (key: string): Promise<boolean> => { | ||
return this.cache.has(key); | ||
}; | ||
|
||
delete = async (key: string): Promise<boolean> => { | ||
return this.cache.delete(key); | ||
}; | ||
|
||
clear = async (): Promise<void> => { | ||
this.cache.clear(); | ||
}; | ||
|
||
findAndClear = async (searchPattern: string): Promise<string[]> => { | ||
return this.cache.findAndClear(searchPattern); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import { type ICache } from "./cache.interface"; | ||
import { createClient, type RedisClientType } from 'redis'; | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////// | ||
// Using KeyDB as a Redis cache | ||
// KeyDB is a high-performance fork of Redis with a focus on multithreading and memory efficiency | ||
// It is designed to be a drop-in replacement for Redis | ||
// KeyDB is fully compatible with Redis and supports all Redis commands | ||
// | ||
// docker run \ | ||
// -d --name keydb \ | ||
// -p 6379:6379 \ | ||
// -e "CACHE_PASSWORD=your-password" \ | ||
// -v /path/to/your/data:/data \ | ||
// -v /path/to/your/logs:/logs keydb/keydb \ | ||
// eqalpha/keydb | ||
|
||
// Process to connect with KeyDB is same as Redis. | ||
// 1. Run the docker container. | ||
// 2. Set the password by logging into container | ||
// a. First run redis-cli as | ||
// ```# redis-cli``` | ||
// b. Set the password using | ||
// ```# auth <your-password>``` | ||
// 3. Create a client and connect to KeyDB. | ||
// 4. Use the client to perform operations. | ||
// 5. Close the connection when done. | ||
// | ||
//////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
export class RedisCache implements ICache { | ||
|
||
private _client: RedisClientType| null = null; | ||
|
||
private _expiry = 60 * 60 * 24 * 2; // 48 hours | ||
|
||
constructor() { | ||
// Create a client and connect to KeyDB | ||
var port = process.env.CACHE_PORT ? parseInt(process.env.CACHE_PORT) : 6379; | ||
this._client = createClient({ | ||
socket: { | ||
host: process.env.CACHE_HOST || 'localhost', | ||
port: port, | ||
}, | ||
password: process.env.CACHE_PASSWORD // if authentication is required | ||
}); | ||
(async () => { | ||
if (this._client) await this._client.connect(); | ||
})(); | ||
} | ||
|
||
set = async (key: string, value: any): Promise<void> => { | ||
if (this._client) { | ||
const exists = await this._client.exists(key); | ||
if (exists === 1) { | ||
await this._client.del(key); | ||
} | ||
await this._client.set(key, JSON.stringify(value), { | ||
EX: this._expiry,// 24 hours | ||
}); | ||
} | ||
}; | ||
|
||
get = async (key: string): Promise<any | undefined> => { | ||
if (this._client) { | ||
const val = await this._client.get(key); | ||
if (val) { | ||
const value = JSON.parse(val); | ||
return value; | ||
} | ||
} | ||
return undefined; | ||
}; | ||
|
||
has = async (key: string): Promise<boolean> => { | ||
if (this._client) { | ||
const exists = await this._client.exists(key); | ||
return exists === 1; | ||
} | ||
return false; | ||
}; | ||
|
||
delete = async (key: string): Promise<boolean> => { | ||
if (this._client) { | ||
const exists = await this._client.exists(key); | ||
if (exists === 1) { | ||
await this._client.del(key); | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
|
||
clear = async (): Promise<void> => { | ||
if (this._client) { | ||
console.log('Clearing cache'); | ||
this._client.flushAll(); | ||
} | ||
}; | ||
|
||
// size = async (): Promise<number> => { | ||
// if (this._client) { | ||
// // console.log('Getting cache size'); | ||
// this._client.dbSize(); | ||
// } | ||
// return 0; | ||
// }; | ||
|
||
findAndClear = async (searchPattern: string): Promise<string[]> => { | ||
if (this._client) { | ||
const keys = await this._client.keys(searchPattern); | ||
if (keys.length > 0) { | ||
await this._client.del(keys); | ||
} | ||
return keys; | ||
} | ||
return []; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.