Skip to content

Commit

Permalink
Allow for array of keys
Browse files Browse the repository at this point in the history
Signed-off-by: Marcos Candeia <[email protected]>
  • Loading branch information
mcandeia committed Oct 1, 2024
1 parent 3250be2 commit dd60c64
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 110 deletions.
3 changes: 3 additions & 0 deletions src/actors/runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ Deno.test("counter tests", async () => {
await ch.close();

const watcher = await actor.watch();

assertEquals(await actor.getCount(), 0);

// Test increment
const number = await actor.increment();
assertEquals(number, 1);
Expand Down
30 changes: 20 additions & 10 deletions src/actors/storage.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export interface ActorStorageListOptions {
start?: string;
startAfter?: string;
end?: string;
prefix?: string;
start?: string[];
startAfter?: string[];
end?: string[];
prefix?: string[];
reverse?: boolean;
limit?: number;
noCache?: boolean;
Expand All @@ -24,23 +24,33 @@ export interface ActorStorage {
options?: ActorStorageGetOptions,
): Promise<T>;
get<T = unknown>(
keys: string[],
key: string[],
options?: ActorStorageGetOptions,
): Promise<Map<string, T>>;
): Promise<T>;
get<T = unknown>(
keys: string[][],
options?: ActorStorageGetOptions,
): Promise<[string[], T][]>;
list<T = unknown>(
options?: ActorStorageListOptions,
): Promise<Map<string, T>>;
): Promise<[string[], T][]>;
put<T>(
key: string,
value: T,
options?: ActorStoragePutOptions,
): Promise<void>;
put<T>(
entries: Record<string, T>,
key: string[],
value: T,
options?: ActorStoragePutOptions,
): Promise<void>;
put<T>(
entries: [string[], T][],
options?: ActorStoragePutOptions,
): Promise<void>;
delete(key: string, options?: ActorStoragePutOptions): Promise<boolean>;
delete(keys: string[], options?: ActorStoragePutOptions): Promise<number>;
delete(key: string[], options?: ActorStoragePutOptions): Promise<boolean>;
delete(key: string[], options?: ActorStoragePutOptions): Promise<boolean>;
delete(keys: string[][], options?: ActorStoragePutOptions): Promise<number>;
deleteAll(options?: ActorStoragePutOptions): Promise<void>;
atomic(storage: (st: ActorStorage) => Promise<void>): Promise<void>;
}
115 changes: 64 additions & 51 deletions src/actors/storage/cached.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,107 +12,120 @@ export class CachedStorage implements ActorStorage {
constructor(protected innerStorage: ActorStorage) {}

private async getMany<T = unknown>(
keys: string[],
keys: string[][],
options?: ActorStorageGetOptions,
): Promise<Map<string, T>> {
): Promise<[string[], T][]> {
const { noCache } = options || {};
const result = new Map<string, T>();
const keysToFetch: string[] = [];
const result: [string[], T][] = [];
const keysToFetch: string[][] = [];

for (const key of keys) {
if (!noCache && this.cache.has(key)) {
result.set(key, this.cache.get(key) as T);
const keyString = this.keyToString(key);
if (!noCache && this.cache.has(keyString)) {
result.push([key, this.cache.get(keyString) as T]);
} else {
keysToFetch.push(key);
}
}

if (keysToFetch.length > 0) {
const fetched = await this.innerStorage.get<T>(keysToFetch, options);
for (const [key, value] of fetched.entries()) {
this.cache.set(key, value);
result.set(key, value);
for (const [key, value] of fetched) {
this.cache.set(this.keyToString(key), value);
result.push([key, value]);
}
}

return result;
}

// Helper function to convert array of strings into a single string for cache key
private keyToString(key: string[]): string {
return key.join(":@:");
}

async get<T = unknown>(
key: string,
options?: ActorStorageGetOptions,
): Promise<T>;
async get<T = unknown>(
keys: string[],
options?: ActorStorageGetOptions,
): Promise<Map<string, T>>;
async get<T = unknown>(
keys: string | string[],
keyOrKeys: string | string[] | string[][],
options?: ActorStorageGetOptions,
): Promise<Map<string, T> | string> {
if (typeof keys === "string") {
const results = await this.getMany<T>([keys], options);
return results.get(keys) as string;
): Promise<T | [string[], T][]> {
if (Array.isArray(keyOrKeys[0])) {
// If the first element is an array, it's a list of keys
return this.getMany(keyOrKeys as string[][], options);
} else {
// Single key case
const results = await this.getMany([
typeof keyOrKeys === "string" ? [keyOrKeys] : keyOrKeys as string[],
], options);
return results[0][1] as T;
}
return this.getMany(keys, options);
}

async list<T = unknown>(
options?: ActorStorageListOptions,
): Promise<Map<string, T>> {
): Promise<[string[], T][]> {
const result = await this.innerStorage.list<T>(options);

for (const [key, value] of result.entries()) {
if (this.cache.has(key)) {
result.set(key, this.cache.get(key));
for (const [key, value] of result) {
const keyString = this.keyToString(key);
if (this.cache.has(keyString)) {
result.push([key, this.cache.get(keyString)]);
} else {
this.cache.set(key, value);
this.cache.set(keyString, value);
}
}

return result;
}

async put<T>(
keyOrEntries: string | Record<string, T>,
keyOrEntries: string | string[] | [string[], T][],
valueOrOptions?: T | ActorStoragePutOptions,
options?: ActorStoragePutOptions,
): Promise<void> {
const entries = typeof keyOrEntries === "string"
? { [keyOrEntries]: valueOrOptions as T }
: keyOrEntries;
// Multiple entries put
const entries: [string[], T][] = Array.isArray(keyOrEntries[0])
? keyOrEntries as [string[], T][]
: [[
typeof keyOrEntries === "string"
? [keyOrEntries]
: keyOrEntries as string[],
valueOrOptions as T,
]];

await this.innerStorage.put(
entries,
(typeof keyOrEntries === "string"
? options
: valueOrOptions) as ActorStoragePutOptions,
(Array.isArray(keyOrEntries[0])
? valueOrOptions
: options) as ActorStoragePutOptions,
);
for (const key in entries) {
this.cache.set(key, entries[key]);

for (const [key, value] of entries) {
this.cache.set(this.keyToString(key), value);
}
}

async delete(
key: string,
key: string[],
options?: ActorStoragePutOptions,
): Promise<boolean>;
async delete(
keys: string[],
keys: string[][],
options?: ActorStoragePutOptions,
): Promise<number>;

async delete(
keyOrKeys: string | string[],
keyOrKeys: string | string[] | string[][],
options?: ActorStoragePutOptions,
): Promise<boolean | number> {
const keys = typeof keyOrKeys === "string" ? [keyOrKeys] : keyOrKeys;
// Multiple keys delete
const result = await this.innerStorage.delete(
keys,
options,
);
keys.forEach((key) => this.cache.delete(key));
return result;
): Promise<number | boolean> {
const keys = Array.isArray(keyOrKeys[0])
? keyOrKeys as string[][]
: [typeof keyOrKeys === "string" ? [keyOrKeys] : keyOrKeys as string[]];

const result = await this.innerStorage.delete(keys, options);

keys.forEach((key) => {
this.cache.delete(this.keyToString(key));
});

return Array.isArray(keyOrKeys[0]) ? result : result > 0;
}

async deleteAll(options?: ActorStoragePutOptions): Promise<void> {
Expand Down
Loading

0 comments on commit dd60c64

Please sign in to comment.