Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

[docs] How Tos update based on new template/format #1109

Merged
merged 22 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from 21 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
17 changes: 10 additions & 7 deletions docs/05_best-practices/04_data-design-and-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,44 @@
content_title: Data design and migration
---

EOSIO based blockchains allow developers to easily update their smart contract code. However, a few things need to be considered when it comes to data update and/or migration. The main structure for storing data in EOSIO based blockchains is the multi index table. Once a multi index table has been created with a first version of a smart contract, it has some limitations when it comes to changing its structure. Below you will find a few possible approaches which you can consider when you design your smart contract data and its migration.
EOSIO based blockchains allow developers to easily update their smart contract code. However, a few things need to be considered when it comes to data updates and/or migration. The multi-index table API is one of the mechanisms (Key-Value API being the other) for storing and updating blockchain state. The multi-index table API creates and uses data structures in RAM. Once created and deployed on the blockchain there are limitations if you want to update these structures. Below you will find a few approaches to your smart contract data design, updates to this design, and for data migration.

# How to modify the structure of a multi index table
# How to modify a data structure defined using multi-index table API

Modifying a multi-index table structure that has already been deployed to an EOSIO-based blockchain may be done by selecting one of the different strategies outlined below, depending on your requirements:
Modifying a deployed multi-index table structure may be done by selecting one of the different strategies outlined below:

## 1. If you don't mind losing the existing data

If you don't mind losing the data from the initial table you can follow these two steps:

1. Erase all records from first table
2. Deploy a new contract with modified table structure

## 2. If you want to keep the existing data

If you want to keep the existing data there are two ways to do it:

### 2.1. Using binary extentions
### 2.1. Using binary extensions

To learn how to modify the structure using binary extensions read this [tutorial](../09_tutorials/01_binary-extension.md).

### 2.2. Using ABI variants

To learn how to modify the structure using ABI variants read this [tutorial](../09_tutorials/02_abi-variants.md).

### 2.3. Migrate the existing data to a second table

#### 2.3.1. Migration without downtime, but slower

1. Create the new version of your multi index table alongside the old one;
1. Create the new version of your multi-index table alongside the old one.
2. Transfer data from the old table to the new one. You may do so as part of your normal access pattern, first checking the new table to see if the entry you seek is present and if not, check the original table, and if it's present, migrate it while adding the data for the new field, then remove it from the original table to save RAM costs.
3. You must retain both versions of your multi index table until you have completed this migration, at which point you may update your contract to remove the original version of your multi index table.
3. You must retain both versions of your multi-index table until you have completed this migration, at which point you may update your contract to remove the original version of your multi-index table.

#### 2.3.2. Migration with downtime, but faster

If you prefer less code complexity and can accept downtime for your application:

1. Deploy a version of your contract solely for migration purposes, and run migration transactions on every row of your table until complete. If the first table is big, e.g. has a large number of rows, the transaction time limit could be reached while running the migration transactions. To mitigate this implement the migrate function to move a limited number of rows each time it runs;
1. Deploy a version of your contract solely for migration purposes, and run migration transactions on every row of your table until complete. If the first table is big, e.g. has a large number of rows, the transaction time limit could be reached while running the migration transactions. To mitigate this implement the migrate function to move a limited number of rows each time it runs.
2. Deploy a new contract using only the new version of the table, at which point, your migration and downtime is complete.

[[caution]]
Expand Down
2 changes: 1 addition & 1 deletion docs/05_best-practices/13_binary-extension.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ struct [[eosio::table]] structure {
}
```

And their corresponding sections in the `.abi` files:
Find below their corresponding sections in the `.abi` files:

**binary_extension_contract.abi**

Expand Down
13 changes: 13 additions & 0 deletions docs/06_how-to-guides/10_compile/01_compile-a-contract-via-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ content_title: How to compile a smart contract via CLI

This guide provides instructions how to compile a smart contract using the command line interface (CLI).

## Reference

See the following code reference:

* The [`eosio-cpp`](https://developers.eos.io/manuals/eosio.cdt/v1.8/command-reference/eosio-cpp) tool.

## Before you begin

* You have the source of the contract saved in a local folder, e.g. `./examples/hello/`
Expand All @@ -25,6 +31,13 @@ Follow the following steps to compile your contract.
eosio-cpp -abigen ../src/hello.cpp -o hello.wasm -I ../include/
```

Where:
- `eosio-cpp` = Is the [`eosio-cpp`](https://developers.eos.io/manuals/eosio.cdt/v1.8/command-reference/eosio-cpp) tool.
- `-abigen` = It instructs the `eosio-cpp` tool to generate ABI file.
- `../src/hello.cpp` = Is the input cpp source file to be compiled.
- `-o hello.wasm` = It instructs the `eosio-cpp` tool who to name the output wasm file.
- `-I ../include/` = It tells `eosio-cpp` tool what the include folder path is, in this particular case it is a relative path.

3. Verify the following two files were generated:

* the compiled binary wasm: `hello.wasm`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ content_title: How to compile a smart contract with CMake

## Overview

This guide provides instructions on how to compile a smart contract with CMake.
This guide provides instructions to compile a smart contract with CMake.

## Before you begin

Expand Down
2 changes: 1 addition & 1 deletion docs/06_how-to-guides/30_key-value-api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The Key-Value API provides a set of C++ classes and structures which facilitates

## Concept

The Key-Value API, or KV API, is a new option for developers to create datastore key value tables on-chain. KV API is more flexible than multi-index and allows developers to search the table in a more human-readable way, unlike multi-index tables where search is over 64-bit values.
The Key-Value API, or KV API, is a new option for developers to create datastore key value tables on-chain. KV API is more flexible than multi-index and allows developers to search for the table in a more human-readable way, unlike multi-index tables where search is over 64-bit values.

On top of flexibility, this new API has a simpler interface and it helps the developer to avoid complex C++ templates constructs.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,4 @@ In conclusion, the above instructions show how to define and use a `Key-Value Ma

## Next Steps

The following option is available when you complete the procedure:

* You [can add values](30_how-to-upsert-into-kv-map.md) in the map object created.
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,5 @@ In conclusion, the above instructions show how to upsert into `Key-Value Map` (`

## Next Steps

The following options are available when you complete the procedure:

* [Verify](70_how-to-find-in-kv-map.md) if the newly inserted `person` actually exists in the map. To accomplish this task use the `find()` function of the `kv_map`.
* [Delete](40_how-to-delete-from-kv-map.md) the newly created or updated `person` from the map. To accomplish this task, use the `erase()` function of the `kv map`.
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,5 @@ In conclusion, the above instructions show how to find an object in `Key-Value M

## Next Steps

The following options are available when you complete the procedure:

* [Update](30_how-to-upsert-into-kv-map.md) the `person` found.
* [Delete](40_how-to-delete-from-kv-map.md) the `person` found.
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,5 @@ In conclusion, the above instructions show how to require the user to pay for th

## Next Steps

The following options are available when you complete the procedure:

* [Verify](70_how-to-find-in-kv-map.md) if the newly inserted `person` actually exists in the map. To accomplish this task use the `find()` function of the `kv_map`.
* [Delete](40_how-to-delete-from-kv-map.md) the newly created or updated `person` from the map. To accomplish this task, use the `erase()` function of the `kv map`.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Complete the following steps to define the `address_table` type, based on the `e
1. Define the structure or class `address_table` in the scope of your smart contract class, for the abi generation to find it and place it into the abi file.
2. Derive `address_table` from `eosio::`kv::table` class template. Pass the `person` user defined type as the type parameter for `eosio::`kv::table` base class and the name of the `kv table`, let’s say `kvaddrbook`.
3. Annotate `address_table` type with `[[eosio::table]]`, and make sure it is placed after the `struct` keyword but before the name of the type.
4. Define a primary index `first_name_idx` based on the property `person::first_name`. Every `kv table` requires a primary index to be defined based on a property that stores unique values.
4. Define a primary index `first_name_idx` based on the data member `person::first_name`. Every `kv table` requires a primary index to be defined on a data member that stores unique values.
5. In the `address_table` constructor, call the `init(...)` base class method with the two parameters:
1. The first parameter, of type `eosio::name`, is the contract that owns the table.
2. The second parameter is the `account_name_uidx` primary index.
Expand Down Expand Up @@ -80,8 +80,6 @@ In conclusion, the above instructions show how to define and use a `Key-Value Ta

## Next Steps

The following options are available when you complete the procedure:

* You can [create one or more unique indexes](20_how-to-create-indexes-kv-table.md) using the `KV_NAMED_INDEX` macro or the `eosio::kv::table::index` template class.
* You can [create one or more non-unique indexes](20_how-to-create-indexes-kv-table.md) using the `KV_NAMED_INDEX` macro or the `eosio::kv::table::index` template class.
* You can access the defined `kv table` and perform operations on it and its defined indexes:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ class [[eosio::contract]] smrtcontract : public contract {

## Procedure

### Define a unique index on property account_name using the macro KV_NAMED_INDEX
### Define a unique index on the data member account_name using the macro KV_NAMED_INDEX

1. Use the `KV_NAMED_INDEX` macro with two parameters.
2. Pass the name of the index as the first parameter. The parameter must be a qualified `eosio::name`. See documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name).
3. Pass the name of the property for which the index is defined as the second parameter.
4. Call `init()` of the base class in the constructor of `address_table` type and pass the contract name as the first parameter and the primary index defined previously as the second parameter.
2. The first parameter is the name of the index and must be a qualified `eosio::name`. See documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name).
3. The second parameter is the name of the data member the index is defined for.
Copy link
Contributor

Choose a reason for hiding this comment

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

The second parameter is the name of the data member the index is defined for.

A bit pedantic but =>

The second parameter is the name of the data member the index is defined ON.

Copy link
Contributor Author

@iamveritas iamveritas Jun 17, 2021

Choose a reason for hiding this comment

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

:) I see them equivalent, no?

Copy link
Contributor

Choose a reason for hiding this comment

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

no ... for is 'clunky'.

Copy link
Contributor

Choose a reason for hiding this comment

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

  • are steps 2 and 3 steps used to define the KV_NAMED_INDEX?

This should be reflected by making 2 and 3 indented below 1.

Copy link
Contributor

Choose a reason for hiding this comment

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

or should 1. even be in a numbered list - isn't this a description of what you are doing?

4. Call the base class method `init()` in the constructor of `address_table` type and pass the contract name as the first parameter and the `account_name` index, using the KV_NAMED_INDEX macro, as the second parameter.

Refer to the following reference implementation of a unique index on property `account_name` using macro `KV_NAMED_INDEX`:
Refer to the following reference implementation of a unique index on `account_name` data member using macro `KV_NAMED_INDEX`:

`smartcontract.hpp file`

Expand All @@ -80,13 +80,14 @@ class [[eosio::contract]] smrtcontract : public contract {
};
```

### Define a unique index on property personal_id using the eosio::kv::table::index template class
### Define a unique index on the data member personal_id using the eosio::kv::table::index template class

1. Use the `eosio::kv::table::index` template class with two parameters.
2. Pass the name of the index as the first parameter. The parameter must be a qualified `eosio:name`. See documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name).
3. Pass the reference to the property for which the index is defined, `&person::personal_id`, as the second parameter.
2. The first parameter is the name of the index and must be a qualified `eosio:name`. See documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name).
3. The second parameter is the name of the data member the index is defined for.
4. Call the base class method `init()` in the constructor of `address_table` type and pass the contract name as the first parameter and the `personal_id_idx` index as the second parameter.

Refer to the following reference implementation of a unique index on property `personal_id` using `eosio::kv::table::index` template class:
Refer to the following reference implementation of a unique index on `personal_id` data member using `eosio::kv::table::index` template class:

`smartcontract.hpp file`

Expand All @@ -107,15 +108,16 @@ class [[eosio::contract]] smrtcontract : public contract {
};
```

### Define a non-unique index on property first_name using the macro KV_NAMED_INDEX
### Define a non-unique index on the data member first_name using the macro KV_NAMED_INDEX

1. Use the `KV_NAMED_INDEX` with two parameters.
2. Pass the name of the index as the first parameter. The parameter must be a qualified `eosio::name`. See documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name).
3. Pass the name of the property for which the index is defined as the second parameter.
2. The first parameter is the name of the index and must be a qualified `eosio::name`. See documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name).
3. The second parameter is the name of the data member the index is defined for.
4. Call the base class method `init()` in the constructor of `address_table` type and pass the contract name as the first parameter and `first_name` index, by the KV_NAMED_INDEX macro, as the second parameter.

The property used for the second parameter must be of template type `std::tuple`. The first parameter must be the type of the property indexed non-uniquely, in our case the type `std::string` is used because `first_name` is the property indexed non-uniquely. And the last parameter of the tuple type must be the type of a property name which is unique. In our case the type `eosio::name` is used because property `account_name` is unique. Multiple properties can be indexed non-uniquely as well. In this case the first parameters types correspond to the properties being indexed. And, as previously already mentioned, the last parameter correspond to the type of a property name which is unique.
The second parameter is the name of the data member the index is defined for and the type of this data member must be `std::tuple<>`. The first parameter of the tuple must be the type of the data member indexed non-uniquely, in our case the type `std::string` is used because `first_name` is the data member indexed non-uniquely. The last parameter of the tuple type must be the type of a data member name which is unique. In our case the type `eosio::name` is used because data member `account_name` is unique. Multiple properties can be indexed non-uniquely as well. In this case the first parameter types correspond to the properties being indexed. As previously already mentioned, the last parameter correspond to the type of a data member name which is unique.

Refer to the following reference implementation of a non-unique index on property `account_name` using macro `KV_NAMED_INDEX`:
Refer to the following reference implementation of a non-unique index on `account_name` data member using macro `KV_NAMED_INDEX`:

`smartcontract.hpp file`

Expand All @@ -141,15 +143,16 @@ class [[eosio::contract]] smrtcontract : public contract {
};
```

### Define a non-unique index on property last_name using the eosio::kv::table::index template class
### Define a non-unique index on the data member last_name using the eosio::kv::table::index template class

1. Use the `eosio::kv::table::index` template class.
2. Pass as the first parameter the name of the index. It must be a qualified `eosio:name`, see documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name).
3. Pass the reference to the property for which the index is defined, `&person::last_name`, as the second parameter.
2. The first parameter is the name of the index and must be a qualified `eosio:name`, see documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name).
3. The second parameter is the name of the data member the index is defined for.
4. Call the base class method `init()` in the constructor of `address_table` type and pass the contract name as the first parameter and the `last_name_idx` index as the second parameter.

The property used for the second parameter must be of template type `std::tuple`. The first parameter must be the type of the property indexed non-uniquely, in our case the type `std::string` is used because `first_name` is the property indexed non-uniquely. And the last parameter of the tuple type must be the type of a property name which is unique. In our case the type `eosio::name` is used because property `account_name` is unique. Multiple properties can be indexed non-uniquely as well. In this case the first parameters types correspond to the properties being indexed. And, as previously already mentioned, the last parameter correspond to the type of a property name which is unique.
The data member used for the second parameter must be of template type `std::tuple`. The first parameter must be the type of the data member indexed non-uniquely, in our case the type `std::string` is used because `first_name` is the data member indexed non-uniquely. The last parameter of the tuple type must be the type of a data member name which is unique. In our case the type `eosio::name` is used because data member `account_name` is unique. Multiple properties can be indexed non-uniquely as well. In this case the first parameter types correspond to the properties being indexed. As previously already mentioned, the last parameter correspond to the type of a data member name which is unique.

Refer to the following reference implementation of a non-unique index on property `last_name` using `eosio::kv::table::index` template class:
Refer to the following reference implementation of a non-unique index `last_name` data member using `eosio::kv::table::index` template class:

`smartcontract.hpp file`

Expand All @@ -169,7 +172,7 @@ class [[eosio::contract]] smrtcontract : public contract {
&person::last_name};

address_table(eosio::name contract_name) {
init(contract_name, last_name)
init(contract_name, last_name_idx)
}
};
public:
Expand All @@ -183,7 +186,5 @@ In conclusion, the above instructions show how to create indexes on a `Key-Value

## Next Steps

The following options are available when you complete the procedure:

* [Search](70_how-to-find-in-kv-table.md) by index key for values or range of values in the defined `kv table`.
* [Check](60_how-to-check-a-record-kv-table.md) if a particular key exists in an index.
Loading