Merge pull request #233 from MARCROCK22/main

encode and decode data in cache
This commit is contained in:
Free 公園 2024-08-03 19:31:31 -05:00 committed by GitHub
commit c54091b2b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 60 additions and 28 deletions

View File

@ -1,11 +1,27 @@
import type { Adapter } from './types'; import type { Adapter } from './types';
export class MemoryAdapter implements Adapter { export interface MemoryAdapterOptions<T> {
encode(data: any): T;
decode(data: T): unknown;
}
export class MemoryAdapter<T> implements Adapter {
isAsync = false; isAsync = false;
readonly storage = new Map<string, string>(); readonly storage = new Map<string, T>();
readonly relationships = new Map<string, string[]>(); readonly relationships = new Map<string, string[]>();
constructor(
public options: MemoryAdapterOptions<T> = {
encode(data) {
return JSON.stringify(data) as T;
},
decode(data) {
return JSON.parse(data as string);
},
},
) {}
scan(query: string, keys?: false): any[]; scan(query: string, keys?: false): any[];
scan(query: string, keys: true): string[]; scan(query: string, keys: true): string[];
scan(query: string, keys = false) { scan(query: string, keys = false) {
@ -13,7 +29,7 @@ export class MemoryAdapter implements Adapter {
const sq = query.split('.'); const sq = query.split('.');
for (const [key, value] of this.storage.entries()) { for (const [key, value] of this.storage.entries()) {
if (key.split('.').every((value, i) => (sq[i] === '*' ? !!value : sq[i] === value))) { if (key.split('.').every((value, i) => (sq[i] === '*' ? !!value : sq[i] === value))) {
values.push(keys ? key : JSON.parse(value)); values.push(keys ? key : this.options.decode(value));
} }
} }
@ -24,24 +40,24 @@ export class MemoryAdapter implements Adapter {
return keys return keys
.map(x => { .map(x => {
const data = this.storage.get(x); const data = this.storage.get(x);
return data ? JSON.parse(data) : null; return data ? this.options.decode(data) : null;
}) })
.filter(x => x); .filter(x => x);
} }
get(keys: string) { get(keys: string) {
const data = this.storage.get(keys); const data = this.storage.get(keys);
return data ? JSON.parse(data) : null; return data ? this.options.decode(data) : null;
} }
bulkSet(keys: [string, any][]) { bulkSet(keys: [string, any][]) {
for (const [key, value] of keys) { for (const [key, value] of keys) {
this.storage.set(key, JSON.stringify(value)); this.storage.set(key, this.options.encode(value));
} }
} }
set(key: string, data: any) { set(key: string, data: any) {
this.storage.set(key, JSON.stringify(data)); this.storage.set(key, this.options.encode(data));
} }
bulkPatch(updateOnly: boolean, keys: [string, any][]) { bulkPatch(updateOnly: boolean, keys: [string, any][]) {
@ -52,7 +68,7 @@ export class MemoryAdapter implements Adapter {
} }
this.storage.set( this.storage.set(
key, key,
Array.isArray(value) ? JSON.stringify(value) : JSON.stringify({ ...(oldData ?? {}), ...value }), Array.isArray(value) ? this.options.encode(value) : this.options.encode({ ...(oldData ?? {}), ...value }),
); );
} }
} }
@ -64,7 +80,7 @@ export class MemoryAdapter implements Adapter {
} }
this.storage.set( this.storage.set(
keys, keys,
Array.isArray(data) ? JSON.stringify(data) : JSON.stringify({ ...(oldData ?? {}), ...data }), Array.isArray(data) ? this.options.encode(data) : this.options.encode({ ...(oldData ?? {}), ...data }),
); );
} }

View File

@ -7,7 +7,7 @@ export interface ResourceLimitedMemoryAdapter {
limit?: number; limit?: number;
} }
export interface LimitedMemoryAdapterOptions { export interface LimitedMemoryAdapterOptions<T> {
default?: ResourceLimitedMemoryAdapter; default?: ResourceLimitedMemoryAdapter;
guild?: ResourceLimitedMemoryAdapter; guild?: ResourceLimitedMemoryAdapter;
@ -26,24 +26,33 @@ export interface LimitedMemoryAdapterOptions {
thread?: ResourceLimitedMemoryAdapter; thread?: ResourceLimitedMemoryAdapter;
overwrite?: ResourceLimitedMemoryAdapter; overwrite?: ResourceLimitedMemoryAdapter;
message?: ResourceLimitedMemoryAdapter; message?: ResourceLimitedMemoryAdapter;
encode(data: any): T;
decode(data: T): unknown;
} }
export class LimitedMemoryAdapter implements Adapter { export class LimitedMemoryAdapter<T> implements Adapter {
isAsync = false; isAsync = false;
readonly storage = new Map<string, LimitedCollection<string, string>>(); readonly storage = new Map<string, LimitedCollection<string, T>>();
readonly relationships = new Map<string, Map<string, string[]>>(); readonly relationships = new Map<string, Map<string, string[]>>();
options: MakeRequired<LimitedMemoryAdapterOptions, 'default'>; options: MakeRequired<LimitedMemoryAdapterOptions<T>, 'default'>;
constructor(options: LimitedMemoryAdapterOptions) { constructor(options: LimitedMemoryAdapterOptions<T>) {
this.options = MergeOptions( this.options = MergeOptions(
{ {
default: { default: {
expire: undefined, expire: undefined,
limit: Number.POSITIVE_INFINITY, limit: Number.POSITIVE_INFINITY,
}, },
} satisfies LimitedMemoryAdapterOptions, encode(data) {
return JSON.stringify(data) as T;
},
decode(data) {
return JSON.parse(data as string);
},
} satisfies LimitedMemoryAdapterOptions<T>,
options, options,
); );
} }
@ -56,7 +65,7 @@ export class LimitedMemoryAdapter implements Adapter {
for (const iterator of [...this.storage.values()].flatMap(x => x.entries())) for (const iterator of [...this.storage.values()].flatMap(x => x.entries()))
for (const [key, value] of iterator) { for (const [key, value] of iterator) {
if (key.split('.').every((value, i) => (sq[i] === '*' ? !!value : sq[i] === value))) { if (key.split('.').every((value, i) => (sq[i] === '*' ? !!value : sq[i] === value))) {
values.push(keys ? key : JSON.parse(value.value)); values.push(keys ? key : this.options.decode(value.value));
} }
} }
@ -68,14 +77,14 @@ export class LimitedMemoryAdapter implements Adapter {
return keys return keys
.map(key => { .map(key => {
const data = iterator.find(x => x.has(key))?.get(key); const data = iterator.find(x => x.has(key))?.get(key);
return data ? JSON.parse(data) : null; return data ? this.options.decode(data) : null;
}) })
.filter(x => x); .filter(x => x);
} }
get(keys: string) { get(keys: string) {
const data = [...this.storage.values()].find(x => x.has(keys))?.get(keys); const data = [...this.storage.values()].find(x => x.has(keys))?.get(keys);
return data ? JSON.parse(data) : null; return data ? this.options.decode(data) : null;
} }
private __set(key: string, data: any) { private __set(key: string, data: any) {
@ -87,9 +96,11 @@ export class LimitedMemoryAdapter implements Adapter {
namespace, namespace,
new LimitedCollection({ new LimitedCollection({
expire: expire:
this.options[key.split('.')[0] as keyof LimitedMemoryAdapterOptions]?.expire ?? this.options.default.expire, this.options[key.split('.')[0] as Exclude<keyof LimitedMemoryAdapterOptions<T>, 'decode' | 'encode'>]
?.expire ?? this.options.default.expire,
limit: limit:
this.options[key.split('.')[0] as keyof LimitedMemoryAdapterOptions]?.limit ?? this.options.default.limit, this.options[key.split('.')[0] as Exclude<keyof LimitedMemoryAdapterOptions<T>, 'decode' | 'encode'>]
?.limit ?? this.options.default.limit,
resetOnDemand: true, resetOnDemand: true,
onDelete(k) { onDelete(k) {
const relationshipNamespace = key.split('.')[0]; const relationshipNamespace = key.split('.')[0];
@ -126,7 +137,7 @@ export class LimitedMemoryAdapter implements Adapter {
); );
} }
this.storage.get(namespace)!.set(key, JSON.stringify(data)); this.storage.get(namespace)!.set(key, this.options.encode(data));
} }
bulkSet(keys: [string, any][]) { bulkSet(keys: [string, any][]) {

View File

@ -141,7 +141,7 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
protected async onPacket(shardId: number, packet: GatewayDispatchPayload) { protected async onPacket(shardId: number, packet: GatewayDispatchPayload) {
Promise.allSettled([ Promise.allSettled([
this.events?.runEvent('RAW', this, packet, shardId, false), this.events?.runEvent('RAW', this, packet, shardId, false),
this.collectors.run('RAW', packet), this.collectors.run('RAW', packet, this),
]); //ignore promise ]); //ignore promise
switch (packet.t) { switch (packet.t) {
//// Cases where we must obtain the old data before updating //// Cases where we must obtain the old data before updating

View File

@ -2,6 +2,8 @@ import { randomUUID } from 'node:crypto';
import type { Awaitable, CamelCase } from '../common'; import type { Awaitable, CamelCase } from '../common';
import type { CallbackEventHandler, CustomEventsKeys, GatewayEvents } from '../events'; import type { CallbackEventHandler, CustomEventsKeys, GatewayEvents } from '../events';
import { error } from 'node:console'; import { error } from 'node:console';
import * as RawEvents from '../events/hooks';
import type { UsingClient } from '../commands';
export type AllClientEvents = CustomEventsKeys | GatewayEvents; export type AllClientEvents = CustomEventsKeys | GatewayEvents;
export type ParseClientEventName<T extends AllClientEvents> = T extends CustomEventsKeys ? T : CamelCase<T>; export type ParseClientEventName<T extends AllClientEvents> = T extends CustomEventsKeys ? T : CamelCase<T>;
@ -98,11 +100,14 @@ export class Collectors {
/**@internal */ /**@internal */
async run<T extends AllClientEvents>( async run<T extends AllClientEvents>(
name: T, name: T,
data: Awaited<Parameters<CallbackEventHandler[ParseClientEventName<T>]>[0]>, raw: Awaited<Parameters<CallbackEventHandler[ParseClientEventName<T>]>[0]>,
client: UsingClient,
) { ) {
const collectors = this.values.get(name); const collectors = this.values.get(name);
if (!collectors) return; if (!collectors) return;
const data = RawEvents[name]?.(client, raw as never) ?? raw;
for (const i of collectors) { for (const i of collectors) {
if (await i.options.filter(data as never)) { if (await i.options.filter(data as never)) {
i.idle?.refresh(); i.idle?.refresh();

View File

@ -357,7 +357,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
protected async onPacket(packet: GatewayDispatchPayload, shardId: number) { protected async onPacket(packet: GatewayDispatchPayload, shardId: number) {
Promise.allSettled([ Promise.allSettled([
this.events?.runEvent('RAW', this, packet, shardId, false), this.events?.runEvent('RAW', this, packet, shardId, false),
this.collectors.run('RAW', packet), this.collectors.run('RAW', packet, this),
]); //ignore promise ]); //ignore promise
switch (packet.t) { switch (packet.t) {
//// Cases where we must obtain the old data before updating //// Cases where we must obtain the old data before updating

View File

@ -111,7 +111,7 @@ export class EventHandler extends BaseHandler {
await Promise.all([ await Promise.all([
this.runEvent(args[0].t as never, args[1], args[0].d, args[2]), this.runEvent(args[0].t as never, args[1], args[0].d, args[2]),
this.client.collectors.run(args[0].t as never, args[0].d as never), this.client.collectors.run(args[0].t as never, args[0].d as never, this.client),
]); ]);
} }
@ -150,15 +150,15 @@ export class EventHandler extends BaseHandler {
async runCustom<T extends CustomEventsKeys>(name: T, ...args: Parameters<CustomEvents[T]>) { async runCustom<T extends CustomEventsKeys>(name: T, ...args: Parameters<CustomEvents[T]>) {
const Event = this.values[name]; const Event = this.values[name];
if (!Event) { if (!Event) {
return this.client.collectors.run(name, args as never); return this.client.collectors.run(name, args as never, this.client);
} }
try { try {
if (Event.data.once && Event.fired) { if (Event.data.once && Event.fired) {
return this.client.collectors.run(name, args as never); return this.client.collectors.run(name, args as never, this.client);
} }
Event.fired = true; Event.fired = true;
this.logger.debug(`executed a custom event [${name}]`, Event.data.once ? 'once' : ''); this.logger.debug(`executed a custom event [${name}]`, Event.data.once ? 'once' : '');
await Promise.all([Event.run(args, this.client), this.client.collectors.run(name, args as never)]); await Promise.all([Event.run(args, this.client), this.client.collectors.run(name, args as never, this.client)]);
} catch (e) { } catch (e) {
await this.onFail(name, e); await this.onFail(name, e);
} }