CanDB is a flexible, performant, and horizontally scalable non-relational multi-canister data store built for the Internet Computer.
CanDB 是一种灵活、高性能且可水平扩展的非关系型多容器数据存储,专为互联网计算机而构建。
Actors in CanDB
CanDB has two types of Actors:
- Service Actors
- The IndexCanister Actor
1) Service Actors
Service Actors can be thought of as microservices, and are responsible for serving your microservice APIs. Each canister instance of a service actor contains an instance of CanDB, storing data and allowing for it to easily trigger auto-scaling.
服务Actors 可以被视为微服务,负责为您的微服务 API 提供服务。 服务Actors 的每个容器实例都包含一个 CanDB 实例,用于存储数据并允许其轻松触发自动缩放。
Figure 1A: Spinning up auto-scaling canister partitions from a service actor
从服务Actors启动自动缩放容器分区
In the Hello-CanDB example, the “HelloService” is our Service Actor and each group is represented by a partition with its own unique partition key (PK). New users to a group are inserted into a specific canister in that group’s partition by using that same group’s PK.
在 Hello-CanDB 示例中,“HelloService”是我们的服务Acto,每个组由一个具有自己唯一分区键 (PK) 的分区表示。 通过使用同一组的 PK,将某个组的新用户插入到该组分区中的特定容器中。
Note that all partitions will automatically scale once their canister auto-scaling limits are hit.
请注意,一旦达到其容器自动缩放限制,所有分区都将自动缩放。
2) The IndexCanister Actor
The IndexCanister Actor is the central piece of any multi-canister CanDB application. It is responsible for 3 main functionalities:
- Holding the mapping association of each partition key to canister or canister(s) (if auto-scaled) 保存每个分区键与一个或多个容器的映射关联(如果自动缩放)
Where: pk - a partition key, c - a canister id
- Spinning up new partitions upon client request 根据客户请求启动新分区
- Auto-scaling existing partitions 自动缩放现有分区
NOTE:
Additional functionalities such as cycle management, canister monitoring, rolling canister upgrades, and canister deletion can also be incorporated into the Index Canister. However, these functionalities can also be incorporated into their own Service Actor microservice (if desired)
注:
cycle管理、canister容器监控、滚动canister容器升级和canister容器删除等附加功能也可以合并到索引容器中。 但是,这些功能也可以合并到它们自己的 Service Actor 微服务中(如果需要)
How Auto-Scaling Works
Canister auto-scaling is set up in 3 places
- Exposed to the developer (2 places)
- Index Canister - autoScale<XXX>ServiceCanister() method (developer actively implements)
- Actor Canister - the specific canister canister storage partition constructor and CanDB stable var initialization (developer keeps this code, but doesn’t need to change anything) 特定容器的容器存储分区构造函数和 CanDB stable var 初始化(开发人员保留此代码,但不需要更改任何内容)
- Inside the CanDB library (1 place, abstracted from the developer)
1) Code Handled by CanDB (internal, not exposed to the developer)
a) CanDB.mo
Under the hood, any call made to the CanDB.put() or CanDB.replace() methods triggers a call to the internal scaleIfAtCapacity() method, which will decide to scale out by checking the following conditions:
- Has the canister already scaled out? (each canister can only auto-scale once)
- Has the canister exceeded it’s auto-scaling sizeLimit (#count or #heapSize)
- #count refers to the number of entities in a canister, which is also the number of unique sort keys
- Has the canister exceeded the HEAP_SIZE_INSERT_LIMIT of 1.25GB
Then if the right combination of these conditions are met (see the internal shouldScale() method), the canister will auto-scale by making a call to the autoScale<XXX>ServiceCanister() hook, which was passed by the IndexCanister as a constructor argument to that canister on instantiation of that CanDB instance.
By design, each canister can only auto-scale one time. After auto-scaling, the canister’s scaling status is set to complete, meaning that further insertions (after messages processed in that same consensus round) are sent to the newly spun up canister.
Updates to existing entity records in a canister storage partition that has already auto-scaled can still happen until the canister hits a heap size limit of 1.75GB, a.k.a. the HEAP_SIZE_UPDATE_LIMIT. This update limit is put in place to prevent a canister overflowing its heap during code upgrades.
NOTE:
If your canister is close to reaching the HEAP_SIZE_UPDATE_LIMIT, it will need to be manually re-partitioned. For this reason, developers should try to avoid repeatedly appending large amounts of data to a single entity record if possible.
b) Code the Developer needs to have (external, exposed to the developer)
i) Index Canister
In the hello-candb example, our IndexCanister is responsible for actually spinning up the new canister storage partition associated with a partitionKey (a.k.a. pk). This spin up is triggered through the autoScaleHelloServiceCanister() hook.
TIP
Prefixing the partition keys of your Actor “services” the same way, allows you to ensure that a certain prefix can be used to spin up a canister instance of a specific canister class, as is show below how partition keys starting with “region” will call the createHelloServiceCanister() method and spin up a HelloService actor canister storage partition.
The code responsible for spinning up a new canister storage partition is shown below, ensuring this canister is initialized with the appropriate parameters.
Passing in the text principal of the IndexCanister tells the new HelloService canister that when it needs to auto-scale, it should call that canister principal’s autoScaleHelloServiceCanister() method to auto-scale out.
ii) Actor Canister Storage Partition
The canister partitionKey and auto-scaling options are passed as arguments to the constructor to an Actor Canister “Service” Storage Partition
Then, these same parameters are passed to a stable instance of CanDB, telling it when to scale out.
CanDB数据库储存构件