Skip to content

Commit

Permalink
feat: add redis adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
kid-icarus committed Mar 2, 2024
1 parent bf4c02c commit 6eb0c27
Show file tree
Hide file tree
Showing 5 changed files with 3,119 additions and 7,068 deletions.
24 changes: 24 additions & 0 deletions packages/automerge-repo-storage-redis/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@automerge/automerge-repo-storage-redis",
"version": "1.0.0",
"description": "Redis storage adapter for Automerge Repo",
"repository": "https://github.com/automerge/automerge-repo/tree/master/packages/automerge-repo-storage-redis",
"author": "Ryan Kois <ryan.kois@gmail.com>",
"license": "MIT",
"type": "module",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"watch": "tsc -w"
},
"dependencies": {
"@automerge/automerge-repo": "workspace:*",
"ioredis": "^5.3.2"
},
"publishConfig": {
"access": "public"
},
"devDependencies": {
"typescript": "^5.3.3"
}
}
95 changes: 95 additions & 0 deletions packages/automerge-repo-storage-redis/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* This module provides a storage adapter for Redis.
*
* @packageDocumentation
*/

import {
Chunk,
StorageAdapter,
type StorageKey,
} from "@automerge/automerge-repo"

import { Redis, RedisOptions } from "ioredis"

export class RedisStorageAdapter extends StorageAdapter {
private redis: Redis

/**
* Create a new {@link RedisStorageAdapter}.
* @param opts - Options to pass to the Redis client.
*/
constructor(
opts?: RedisOptions
) {
super()
this.redis = new Redis(opts)
this.redis.on("error", (err) => {
console.error(err)
})
this.redis.on('connect', () => {
console.log('Redis connected')
})
this.redis.on('close', () => {
console.log('Redis closed')
})
this.redis.on('end', () => {
console.log('Redis end')
})
}

async load(keyArray: StorageKey): Promise<Uint8Array | undefined> {
const data = await this.redis.getBuffer(keyArray.join(":"))
return data
}

async save(keyArray: string[], binary: Uint8Array): Promise<void> {
console.log('save', binary.length, keyArray.join(":"))
await this.redis.set(keyArray.join(":"), Buffer.from(binary))
}

async remove(keyArray: string[]): Promise<void> {
await this.redis.del(keyArray.join(":"))
}

async loadRange(keyPrefix: string[]): Promise<Chunk[]> {
const lowerBound = [...keyPrefix, '*'].join(":")
const result: Chunk[] = []
const stream = this.redis.scanStream({
match: lowerBound,
})
stream.on("data", async (keys: string[]) => {
for (const key of keys) {
stream.pause()
const data = await this.redis.getBuffer(key)
result.push({
key: key.split(":"),
data,
})
stream.resume()
}
})
return await new Promise((resolve, reject) => {
stream.on("end", () => resolve(result))
stream.on("error", (err) => reject(err))
})
}

async removeRange(keyPrefix: string[]): Promise<void> {
const lowerBound = [...keyPrefix, '*'].join(":")
const stream = this.redis.scanStream({
match: lowerBound,
})
stream.on("data", async (keys: string[]) => {
for (const key of keys) {
stream.pause()
await this.redis.del(key)
stream.resume()
}
})
return await new Promise((resolve, reject) => {
stream.on("end", resolve)
stream.on("error", reject)
})
}
}
15 changes: 15 additions & 0 deletions packages/automerge-repo-storage-redis/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "Node16",
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": false,
"skipLibCheck": true
},
"include": ["src/**/*.ts"]
}
5 changes: 5 additions & 0 deletions packages/automerge-repo-storage-redis/typedoc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": ["../../typedoc.base.json"],
"entryPoints": ["src/index.ts"],
"readme": "none"
}
Loading

0 comments on commit 6eb0c27

Please sign in to comment.