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

docs: asset canister to split up very large asset contents into chunks for retrieval #1428

Merged
merged 12 commits into from
Feb 24, 2021
Merged
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
99 changes: 53 additions & 46 deletions docs/design/asset-canister.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,18 @@ Store per asset:
* Content type
* Content for one or more content encodings
** Content encoding
** Content blob
** The actual content (bytes)
*** Split up into chunks for retrieval

=== Implementation

We will implement the canister in Motoko using existing stable memory data structures such as RBTree or Trie.
We will implement the canister in Motoko, because the canister's functionality
is within the realm of what might be found in a typical application canister.

=== Considered Solutions

==== Motoko canister

* The API will be straightforward to implement using existing stable-memory data structures
* Currently there is not a large body of Motoko libraries for things like compression
** Mitigated by: compression would probably be better done in dfx anyway

Expand Down Expand Up @@ -140,107 +141,113 @@ canister per block height.
[source,candid]
----

type Contents = blob;
type BlobId = text;
type BatchId = nat;
type ChunkId = nat;
type Key = text;

type AssetInfo = record {
key: Key;
content_type: text;
encoding_details: vec record {
content_encoding: text;
};
};

// Create a new asset. Contents will be attached later with SetContent.
// - No-op if asset already exists with the same content type.
// - Error if asset already exists with a different content type (delete first).
type CreateAssetOperation = record {
type CreateAssetArguments = record {
key: Key;
content_type: text;
};

// Add or change content for an asset, by content encoding
type SetAssetContentOperation = record {
type SetAssetContentArguments = record {
key: Key;
content_encoding: text;
blob_id: BlobId;
chunk_ids: vec ChunkId;
};

// Remove content for an asset, by content encoding
type UnsetAssetContentOperation = record {
type UnsetAssetContentArguments = record {
key: Key;
content_encoding: text;
};

// Delete an asset
type DeleteAssetOperation = record {
type DeleteAssetArguments = record {
key: Key;
};

// Future: set up access control
type SetAssetAclOperation = record {
type SetAssetAclArguments = record {
key: Key;
tbd: text;
};

// Future: set a time after which to delete an asset
type SetAssetExpiryOperation = record {
type SetAssetExpiryArguments = record {
key: Key;
tbd: text;
};

// Reset everything
type ClearOperation = record {};
type ClearArguments = record {};

type BatchOperationKind = variant {
Create: CreateAssetOperation;
SetContent: SetAssetContentOperation;
CreateAsset: CreateAssetArguments;
SetAssetContent: SetAssetContentArguments;

UnsetContent: UnsetAssetContentOperation;
Delete: DeleteAssetOperation;
UnsetAssetContent: UnsetAssetContentArguments;
DeleteAsset: DeleteAssetArguments;

SetAcl: SetAssetAclOperation;
SetExpiry: SetAssetExpiryOperation;
SetAssetAcl: SetAssetAclArguments;
SetAssetExpiry: SetAssetExpiryArguments;

Clear: ClearOperation;
Clear: ClearArguments;
};

service: {

get: (record {
key: Key,
accept_encodings: vec text
}) -> (record { contents: blob; content_type: text; content_encoding: text }) query;
}) -> (record {
content: blob; // may be the entirety of the content, or just chunk index 0
content_type: text;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking, but after today's discussion with @roman-kashitsyn we might want to refactor this. I don't expect a lot of changes here, this is just a heads up.

content_encoding: text,
total_length: nat // all chunks except last have size == content.size()
}) query;

// if get() returned chunks > 1, call this to retrieve them.
// chunks may or may not be split up at the same boundaries as presented to create_chunk().
get_chunk: (record {
key: Key,
content_encoding: text,
index: nat
}) -> (record { content: blob }) query;

list: (record {}) -> (vec AssetInfo) query;
list: (record {}) -> (vec record {
key: Key;
content_type: text;
encodings: vec record {
content_encoding: text;
};
}) query;

// allocate space for content
//
// first deletes all blobs created by an earlier call to create_blobs(), that have not been set to an asset,
// only if write_blob() has not been called for any of those blobs within the past 5 minutes.
create_blobs: (record { blob_info: vec record { length: nat32 } } ) -> (record { blob_ids: vec BlobId });
create_batch(record {}) -> (record { batch_id: BatchId });

// upload part of a blob's content
write_blob: (record { blob_id: BlobId; offset: nat32; contents: blob }) -> ();
create_chunk: (record { batch_id: BatchId; content: blob }) -> (record { chunk_id: ChunkId });

// Perform all operations successfully, or reject
batch: (vec BatchOperationKind) -> ();
commit_batch: (record { batch_id: BatchId; operations: vec BatchOperationKind }) -> ();

create_asset: (CreateAssetOperation) -> ();
set_asset_content: (SetAssetContentOperation) -> ();
unset_asset_content: (UnsetAssetContentOperation) -> ();
create_asset: (CreateAssetArguments) -> ();
set_asset_content: (SetAssetContentArguments) -> ();
unset_asset_content: (UnsetAssetContentArguments) -> ();

delete_asset: (DeleteAssetOperation) -> ();
delete_asset: (DeleteAssetArguments) -> ();

set_asset_acl: (SetAssetAclOperation) -> ();
set_asset_expiry: (SetAssetExpiryOperation) -> ();
set_asset_acl: (SetAssetAclArguments) -> ();
set_asset_expiry: (SetAssetExpiryArguments) -> ();

clear: (ClearOperation) -> ();
clear: (ClearArguments) -> ();

// Single call to create an asset with content for a single content encoding that
// fits within the message ingress limit.
store: (record { key: Key; content_type: text; content_encoding: text; contents: blob }) -> ();
store: (record { key: Key; content_type: text; content_encoding: text; content: blob }) -> ();
}

----
Expand Down