Skip to content

Commit

Permalink
Extend migration example for MerkleDB [ECR-4028] (exonum#1676)
Browse files Browse the repository at this point in the history
  • Loading branch information
Renat Mukhametzanov authored and Oleksandr Anyshchenko committed Jan 9, 2020
1 parent 9254a2a commit 0e793fa
Show file tree
Hide file tree
Showing 6 changed files with 443 additions and 123 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,9 @@ jobs:
# Run examples.
- name: examples
script:
- cargo run -p exonum-merkledb --example migration
- cargo run -p exonum-merkledb --example manual_migration
- cargo run -p exonum-merkledb --example migration_with_helper
- cargo run -p exonum-merkledb --example migration_with_iter_loop
- cargo run -p exonum-merkledb --example blockchain
- cargo run -p exonum-sample-runtime
- cargo run -p exonum-explorer --example explorer
Expand Down
3 changes: 0 additions & 3 deletions components/merkledb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,5 @@ rocksdb_zlib = ["rocksdb/zlib"]
rocksdb_zstd = ["rocksdb/zstd"]
rocksdb_bzip2 = ["rocksdb/bzip2"]

[[example]]
name = "blockchain"

[build-dependencies]
exonum-build = { version = "0.13.0-rc.2", path = "../build" }
108 changes: 108 additions & 0 deletions components/merkledb/examples/manual_migration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2020 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Shows how to provide database data migration manually.
//!
//! The main logic of this example is described in the `manual_migration`
//! and `migrate_wallets` functions.
//!
//! The main points of this example are:
//! - We have to create `Fork` from the DB manually,
//! as well as `Migration` and `Prefixed` access to the data.
//! - We have to apply `Patch` to the DB manually.
//! - Data migration is performed by direct access to old and new schemas.
//!
//! For the description of the common migration scenario, see the `migration` module docs.

use exonum_merkledb::{
access::Prefixed, migration::Migration, Database, Fork, ObjectHash, ReadonlyFork,
};

use std::sync::Arc;

use migration::{perform_migration, v1, v2};

mod migration;

/// Provides migration of wallets with schema.
///
/// `Wallet::public_key` field will be removed.
/// `Wallet::history_hash` field will be added.
/// Wallets and history from username Eve will be removed.
fn migrate_wallets(new_data: Migration<&Fork>, old_data: Prefixed<ReadonlyFork>) {
let old_schema = v1::Schema::new(old_data);
let mut new_schema = v2::Schema::new(new_data);

// Migrate wallets.
for (i, (public_key, wallet)) in old_schema.wallets.iter().enumerate() {
if wallet.username == "Eve" {
// We don't like Eves 'round these parts. Remove her transaction history
// and don't migrate the wallet.
new_data.create_tombstone(("histories", &public_key));
} else {
// Merkelize the wallet history.
let mut history = new_schema.histories.get(&public_key);
history.extend(&old_schema.histories.get(&public_key));

let new_wallet = v2::Wallet {
username: wallet.username,
balance: wallet.balance,
history_hash: history.object_hash(),
};
new_schema.wallets.put(&public_key, new_wallet);
}

if i % 1_000 == 999 {
println!("Processed {} wallets", i + 1);
}
}
}

fn manual_migration(db: Arc<dyn Database>) {
// Create fork to apply changes to it.
let fork = db.fork();

{
let new_data = Migration::new("test", &fork);
let old_data = Prefixed::new("test", fork.readonly());

let old_schema = v1::Schema::new(old_data);
let mut new_schema = v2::Schema::new(new_data);

// Move `ticker` and `divisibility` to `config`.
let config = v2::Config {
ticker: old_schema.ticker.get().unwrap(),
divisibility: old_schema.divisibility.get().unwrap_or(0),
};
new_schema.config.set(config);
// Mark these two indexes for removal.
new_data.create_tombstone("ticker");
new_data.create_tombstone("divisibility");
}

// Migrate wallets using schema:
// `Wallet::public_key` field will be removed.
// `Wallet::history_hash` field will be added.
// Wallets and history from username Eve will be removed.
let new_data = Migration::new("test", &fork);
let old_data = Prefixed::new("test", fork.readonly());
migrate_wallets(new_data, old_data);

// Merge patch with migrated data.
db.merge(fork.into_patch()).unwrap();
}

fn main() {
perform_migration(manual_migration);
}
Loading

0 comments on commit 0e793fa

Please sign in to comment.