Skip to content

Commit

Permalink
feat: Accounts ProxyDb has authoritative/secondary DB (#3975)
Browse files Browse the repository at this point in the history
# Motivation
The `ProxyDb` acts as an intermediary during a migration, keeping data
in the main database up to date until a second database is ready. The
implementation is however currently partial and does not store the main
and secondary db.

# Changes
- This PR fills out the contents of the ProxyDB struct so that we now
actually have these two databases.

# Tests
- Existing CI should suffice

# Todos

- [ ] Add entry to changelog (if necessary).
  • Loading branch information
bitdivine authored Dec 7, 2023
1 parent 627e885 commit fca69c8
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 14 deletions.
56 changes: 42 additions & 14 deletions rs/backend/src/accounts_store/schema/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! The proxy manages migrations from one implementation to another.
use std::collections::BTreeMap;

mod enum_boilerplate;
use super::{map::AccountsDbAsMap, Account, AccountsDbBTreeMapTrait, AccountsDbTrait, SchemaLabel};

/// An accounts database delegates API calls to underlying implementations.
Expand All @@ -16,9 +16,24 @@ use super::{map::AccountsDbAsMap, Account, AccountsDbBTreeMapTrait, AccountsDbTr
///
/// # Current data storage
/// - Accounts are stored as a map. No migrations are undertaken.
#[derive(Default, Debug)]
#[derive(Debug)]
pub struct AccountsDbAsProxy {
map: AccountsDbAsMap,
authoritative_db: AccountsDb,
second_db: Option<AccountsDb>,
}

impl Default for AccountsDbAsProxy {
fn default() -> Self {
Self {
authoritative_db: AccountsDb::Map(AccountsDbAsMap::default()),
second_db: None,
}
}
}

#[derive(Debug)]
pub enum AccountsDb {
Map(AccountsDbAsMap),
}

impl AccountsDbAsProxy {
Expand All @@ -31,46 +46,59 @@ impl AccountsDbAsProxy {
impl AccountsDbBTreeMapTrait for AccountsDbAsProxy {
fn from_map(map: BTreeMap<Vec<u8>, Account>) -> Self {
Self {
map: AccountsDbAsMap::from_map(map),
authoritative_db: AccountsDb::Map(AccountsDbAsMap::from_map(map)),
second_db: None,
}
}
fn as_map(&self) -> &BTreeMap<Vec<u8>, Account> {
self.map.as_map()
match &self.authoritative_db {
AccountsDb::Map(map_db) => map_db.as_map(),
}
}
}

impl AccountsDbTrait for AccountsDbAsProxy {
/// Inserts into all the underlying databases.
fn db_insert_account(&mut self, account_key: &[u8], account: Account) {
self.map.db_insert_account(account_key, account);
self.authoritative_db.db_insert_account(account_key, account.clone());
if let Some(db) = &mut self.second_db {
db.db_insert_account(account_key, account);
}
}
/// Checks the authoritative database.
fn db_contains_account(&self, account_key: &[u8]) -> bool {
self.map.db_contains_account(account_key)
self.authoritative_db.db_contains_account(account_key)
}
/// Gets an account from the authoritative database.
fn db_get_account(&self, account_key: &[u8]) -> Option<Account> {
self.map.db_get_account(account_key)
self.authoritative_db.db_get_account(account_key)
}
/// Removes an account from all underlying databases.
fn db_remove_account(&mut self, account_key: &[u8]) {
self.map.db_remove_account(account_key);
self.authoritative_db.db_remove_account(account_key);
if let Some(db) = self.second_db.as_mut() {
db.db_remove_account(account_key);
}
}
/// Gets the length from the authoritative database.
fn db_accounts_len(&self) -> u64 {
self.map.db_accounts_len()
self.authoritative_db.db_accounts_len()
}
/// Iterates over the entries of the authoritative database.
fn iter(&self) -> Box<dyn Iterator<Item = (Vec<u8>, Account)> + '_> {
self.map.iter()
self.authoritative_db.iter()
}
/// Iterates over the values of the authoritative database.
fn values(&self) -> Box<dyn Iterator<Item = Account> + '_> {
self.map.values()
self.authoritative_db.values()
}
/// The authoritative schema label.
fn schema_label(&self) -> SchemaLabel {
self.map.schema_label()
let schema_label = self.authoritative_db.schema_label();
dfn_core::api::print(format!(
"AccountsDb::Proxy: authoritative schema label: {schema_label:#?}"
));
schema_label
}
}

Expand All @@ -79,7 +107,7 @@ impl AccountsDbTrait for AccountsDbAsProxy {
/// It should be possible to use this to confirm that data has been preserved during a migration.
impl PartialEq for AccountsDbAsProxy {
fn eq(&self, other: &Self) -> bool {
self.map == other.map
self.authoritative_db.as_map() == other.authoritative_db.as_map()
}
}
impl Eq for AccountsDbAsProxy {}
Expand Down
54 changes: 54 additions & 0 deletions rs/backend/src/accounts_store/schema/proxy/enum_boilerplate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! Boilerplate for implementing traits for the AccountsDb enum.
//!
//! Each function is implemented by calling the same function on the applicable variant. There is probably a macro for this.
use super::*;

impl AccountsDbBTreeMapTrait for AccountsDb {
fn as_map(&self) -> &BTreeMap<Vec<u8>, Account> {
match self {
AccountsDb::Map(map_db) => map_db.as_map(),
}
}
fn from_map(map: BTreeMap<Vec<u8>, Account>) -> Self {
AccountsDb::Map(AccountsDbAsMap::from_map(map))
}
}

// TODO: This is boilerplate. can it be eliminated with a macro?
impl AccountsDbTrait for AccountsDb {
fn schema_label(&self) -> SchemaLabel {
match &self {
AccountsDb::Map(map_db) => map_db.schema_label(),
}
}
fn db_insert_account(&mut self, account_key: &[u8], account: Account) {
match self {
AccountsDb::Map(map_db) => map_db.db_insert_account(account_key, account),
}
}
fn db_contains_account(&self, account_key: &[u8]) -> bool {
match self {
AccountsDb::Map(map_db) => map_db.db_contains_account(account_key),
}
}
fn db_get_account(&self, account_key: &[u8]) -> Option<Account> {
match self {
AccountsDb::Map(map_db) => map_db.db_get_account(account_key),
}
}
fn db_remove_account(&mut self, account_key: &[u8]) {
match self {
AccountsDb::Map(map_db) => map_db.db_remove_account(account_key),
}
}
fn db_accounts_len(&self) -> u64 {
match self {
AccountsDb::Map(map_db) => map_db.db_accounts_len(),
}
}
fn iter(&self) -> Box<dyn Iterator<Item = (Vec<u8>, Account)> + '_> {
match self {
AccountsDb::Map(map_db) => map_db.iter(),
}
}
}

0 comments on commit fca69c8

Please sign in to comment.