Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add arbitrary key-value storage #217

Merged
merged 6 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/automerge-repo/src/Repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export class Repo extends EventEmitter<RepoEvents> {
// synchronizer.removeDocument(documentId)

if (storageSubsystem) {
storageSubsystem.remove(documentId).catch(err => {
storageSubsystem.removeDoc(documentId).catch(err => {
this.#log("error deleting document", { documentId, err })
})
}
Expand Down
12 changes: 11 additions & 1 deletion packages/automerge-repo/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,22 @@ export type {
DocHandleOutboundEphemeralMessagePayload,
HandleState,
} from "./DocHandle.js"

export type {
DeleteDocumentPayload,
DocumentPayload,
RepoConfig,
RepoEvents,
SharePolicy,
} from "./Repo.js"

export type {
NetworkAdapterEvents,
OpenPayload,
PeerCandidatePayload,
PeerDisconnectedPayload,
} from "./network/NetworkAdapter.js"

export type {
DocumentUnavailableMessage,
EphemeralMessage,
Expand All @@ -73,5 +76,12 @@ export type {
RequestMessage,
SyncMessage,
} from "./network/messages.js"
export type { StorageKey } from "./storage/StorageAdapter.js"

export type {
Chunk,
ChunkInfo,
ChunkType,
StorageKey,
} from "./storage/types.js"

export * from "./types.js"
53 changes: 23 additions & 30 deletions packages/automerge-repo/src/storage/StorageAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,34 @@
import { StorageKey, Chunk } from "./types.js"

/** A storage adapter represents some way of storing binary data for a {@link Repo}
*
* @remarks
* `StorageAdapter`s are a little like a key/value store. The keys are arrays
* of strings ({@link StorageKey}) and the values are binary blobs.
* `StorageAdapter`s provide a key/value storage interface. The keys are arrays of strings
* ({@link StorageKey}) and the values are binary blobs.
*/
export abstract class StorageAdapter {
// load, store, or remove a single binary blob based on an array key
// automerge-repo mostly uses keys in the following form:
// [documentId, "snapshot"] or [documentId, "incremental", "0"]
// but the storage adapter is agnostic to the meaning of the key
// and we expect to store other data in the future such as syncstates
/** Load the single blob correspongind to `key` */
/** Load the single value corresponding to `key` */
abstract load(key: StorageKey): Promise<Uint8Array | undefined>
/** save the blod `data` to the key `key` */

/** Save the value `data` to the key `key` */
abstract save(key: StorageKey, data: Uint8Array): Promise<void>
/** remove the blob corresponding to `key` */

/** Remove the value corresponding to `key` */
abstract remove(key: StorageKey): Promise<void>

// the keyprefix will match any key that starts with the given array
// for example, [documentId, "incremental"] will match all incremental saves
// or [documentId] will match all data for a given document
// be careful! this will also match [documentId, "syncState"]!
// (we aren't using this yet but keep it in mind.)
/** Load all blobs with keys that start with `keyPrefix` */
abstract loadRange(keyPrefix: StorageKey): Promise<{key: StorageKey, data: Uint8Array}[]>
/** Remove all blobs with keys that start with `keyPrefix` */
/**
* Load all values with keys that start with `keyPrefix`.
*
* @remarks
* The `keyprefix` will match any key that starts with the given array. For example:
* - `[documentId, "incremental"]` will match all incremental saves
* - `[documentId]` will match all data for a given document.
*
* Be careful! `[documentId]` would also match something like `[documentId, "syncState"]`! We
* aren't using this yet but keep it in mind.)
*/
abstract loadRange(keyPrefix: StorageKey): Promise<Chunk[]>

/** Remove all values with keys that start with `keyPrefix` */
abstract removeRange(keyPrefix: StorageKey): Promise<void>
}

/** The type of keys for a {@link StorageAdapter}
*
* @remarks
* Storage keys are arrays because they are hierarchical and the storage
* subsystem will need to be able to do range queries for all keys that
* have a particular prefix. For example, incremental changes for a given
* document might be stored under `[<documentId>, "incremental", <SHA256>]`.
* `StorageAdapter` implementations should not assume any particular structure
* though.
**/
export type StorageKey = string[]
Loading