Skip to content

Commit 95d8654

Browse files
committed
Add migration to v0.26.0
1 parent 6885143 commit 95d8654

File tree

5 files changed

+330
-2
lines changed

5 files changed

+330
-2
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "forest-filecoin"
3-
version = "0.25.0"
3+
version = "0.26.0"
44
authors = ["ChainSafe Systems <info@chainsafe.io>"]
55
repository = "https://github.com/ChainSafe/forest"
66
edition = "2021"

src/db/migration/migration_map.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::{
88
};
99

1010
use crate::db::migration::v0_22_1::Migration0_22_0_0_22_1;
11+
use crate::db::migration::v0_26_0::Migration0_25_0_0_26_0;
1112
use crate::Config;
1213
use anyhow::bail;
1314
use anyhow::Context as _;
@@ -154,6 +155,7 @@ pub(super) static MIGRATIONS: Lazy<MigrationsMap> = Lazy::new(|| {
154155

155156
create_migrations!(
156157
"0.22.0" -> "0.22.1" @ Migration0_22_0_0_22_1,
158+
"0.25.0" -> "0.26.0" @ Migration0_25_0_0_26_0,
157159
);
158160

159161
/// Creates a migration chain from `start` to `goal`. The chain is chosen to be the shortest

src/db/migration/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
mod db_migration;
55
mod migration_map;
66
mod v0_22_1;
7+
mod v0_26_0;
78
mod void_migration;
89

910
pub use db_migration::DbMigration;

src/db/migration/v0_26_0.rs

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
// Copyright 2019-2025 ChainSafe Systems
2+
// SPDX-License-Identifier: Apache-2.0, MIT
3+
4+
//! Migration logic for 0.25.0 to 0.26.0 version.
5+
//! A new `Indices` column has been added to enable the storage of CID-indexed data.
6+
7+
use crate::blocks::TipsetKey;
8+
use crate::db::db_engine::Db;
9+
use crate::db::migration::migration_map::MigrationOperationExt as _;
10+
use crate::db::migration::v0_26_0::paritydb_0_25_0::{DbColumn, ParityDb};
11+
use crate::db::CAR_DB_DIR_NAME;
12+
use crate::rpc::eth::types::EthHash;
13+
use crate::utils::multihash::prelude::*;
14+
use crate::Config;
15+
use anyhow::Context;
16+
use cid::Cid;
17+
use fs_extra::dir::CopyOptions;
18+
use fvm_ipld_encoding::DAG_CBOR;
19+
use semver::Version;
20+
use std::path::{Path, PathBuf};
21+
use strum::IntoEnumIterator;
22+
use tracing::info;
23+
24+
use super::migration_map::MigrationOperation;
25+
26+
pub(super) struct Migration0_25_0_0_26_0 {
27+
from: Version,
28+
to: Version,
29+
}
30+
31+
/// Migrates the database from version 0.25.0 to 0.26.0
32+
impl MigrationOperation for Migration0_25_0_0_26_0 {
33+
fn new(from: Version, to: Version) -> Self
34+
where
35+
Self: Sized,
36+
{
37+
Self { from, to }
38+
}
39+
40+
fn from(&self) -> &Version {
41+
&self.from
42+
}
43+
44+
fn to(&self) -> &Version {
45+
&self.to
46+
}
47+
48+
fn migrate_core(&self, chain_data_path: &Path, _: &Config) -> anyhow::Result<PathBuf> {
49+
let old_db = self.old_db_path(chain_data_path);
50+
let temp_db = self.temporary_db_path(chain_data_path);
51+
52+
let old_car_db_path = old_db.join(CAR_DB_DIR_NAME);
53+
let temp_car_db_path = temp_db.join(CAR_DB_DIR_NAME);
54+
55+
// Make sure `car_db` dir exists as it might not be the case when migrating
56+
// from older versions.
57+
if old_car_db_path.is_dir() {
58+
info!(
59+
"Copying snapshot from {} to {}",
60+
old_db.display(),
61+
temp_db.display()
62+
);
63+
64+
fs_extra::copy_items(
65+
&[old_car_db_path.as_path()],
66+
temp_car_db_path,
67+
&CopyOptions::default().copy_inside(true),
68+
)?;
69+
}
70+
71+
let db = ParityDb::open(old_db)?;
72+
73+
// open the new database to migrate data from the old one.
74+
let new_db = paritydb_0_26_0::ParityDb::open(&temp_db)?;
75+
76+
for col in DbColumn::iter() {
77+
info!("Migrating column {}", col);
78+
let mut res = anyhow::Ok(());
79+
match col {
80+
DbColumn::GraphDagCborBlake2b256 | DbColumn::PersistentGraph => {
81+
db.db.iter_column_while(col as u8, |val| {
82+
let hash = MultihashCode::Blake2b256.digest(&val.value);
83+
let cid = Cid::new_v1(DAG_CBOR, hash);
84+
res = new_db
85+
.db
86+
.commit_changes([Db::set_operation(
87+
col as u8,
88+
cid.to_bytes(),
89+
val.value,
90+
)])
91+
.context("failed to commit");
92+
93+
if res.is_err() {
94+
return false;
95+
}
96+
97+
true
98+
})?;
99+
res?;
100+
}
101+
DbColumn::EthMappings => {
102+
db.db.iter_column_while(col as u8, |val| {
103+
let tsk: Result<TipsetKey, fvm_ipld_encoding::Error> =
104+
fvm_ipld_encoding::from_slice(&val.value);
105+
if tsk.is_err() {
106+
res = Err(tsk.context("serde error").unwrap_err());
107+
return false;
108+
}
109+
let cid = tsk.unwrap().cid();
110+
111+
if cid.is_err() {
112+
res = Err(cid.context("serde error").unwrap_err());
113+
return false;
114+
}
115+
116+
let hash: EthHash = cid.unwrap().into();
117+
res = new_db
118+
.db
119+
.commit_changes([Db::set_operation(
120+
col as u8,
121+
hash.0.as_bytes().to_vec(),
122+
val.value,
123+
)])
124+
.context("failed to commit");
125+
126+
if res.is_err() {
127+
return false;
128+
}
129+
130+
true
131+
})?;
132+
res?;
133+
}
134+
_ => {
135+
let mut iter = db.db.iter(col as u8)?;
136+
while let Some((key, value)) = iter.next()? {
137+
new_db
138+
.db
139+
.commit_changes([Db::set_operation(col as u8, key, value)])
140+
.context("failed to commit")?;
141+
}
142+
}
143+
}
144+
}
145+
146+
drop(new_db);
147+
148+
Ok(temp_db)
149+
}
150+
}
151+
152+
/// Database settings from Forest `v0.25.0`
153+
mod paritydb_0_25_0 {
154+
use parity_db::{CompressionType, Db, Options};
155+
use std::path::PathBuf;
156+
use strum::{Display, EnumIter, IntoEnumIterator};
157+
158+
#[derive(Copy, Clone, Debug, PartialEq, EnumIter, Display)]
159+
#[repr(u8)]
160+
pub(super) enum DbColumn {
161+
GraphDagCborBlake2b256,
162+
GraphFull,
163+
Settings,
164+
EthMappings,
165+
PersistentGraph,
166+
}
167+
168+
impl DbColumn {
169+
fn create_column_options(compression: CompressionType) -> Vec<parity_db::ColumnOptions> {
170+
DbColumn::iter()
171+
.map(|col| {
172+
match col {
173+
DbColumn::GraphDagCborBlake2b256 | DbColumn::PersistentGraph => {
174+
parity_db::ColumnOptions {
175+
preimage: true,
176+
compression,
177+
..Default::default()
178+
}
179+
}
180+
DbColumn::GraphFull => parity_db::ColumnOptions {
181+
preimage: true,
182+
// This is needed for key retrieval.
183+
btree_index: true,
184+
compression,
185+
..Default::default()
186+
},
187+
DbColumn::Settings => {
188+
parity_db::ColumnOptions {
189+
// explicitly disable preimage for settings column
190+
// othewise we are not able to overwrite entries
191+
preimage: false,
192+
// This is needed for key retrieval.
193+
btree_index: true,
194+
compression,
195+
..Default::default()
196+
}
197+
}
198+
DbColumn::EthMappings => parity_db::ColumnOptions {
199+
preimage: false,
200+
btree_index: false,
201+
compression,
202+
..Default::default()
203+
},
204+
}
205+
})
206+
.collect()
207+
}
208+
}
209+
210+
pub(super) struct ParityDb {
211+
pub db: parity_db::Db,
212+
}
213+
214+
impl ParityDb {
215+
pub(super) fn to_options(path: PathBuf) -> Options {
216+
Options {
217+
path,
218+
sync_wal: true,
219+
sync_data: true,
220+
stats: false,
221+
salt: None,
222+
columns: DbColumn::create_column_options(CompressionType::Lz4),
223+
compression_threshold: [(0, 128)].into_iter().collect(),
224+
}
225+
}
226+
227+
pub(super) fn open(path: impl Into<PathBuf>) -> anyhow::Result<Self> {
228+
let opts = Self::to_options(path.into());
229+
Ok(Self {
230+
db: Db::open_or_create(&opts)?,
231+
})
232+
}
233+
}
234+
}
235+
236+
/// Database settings from Forest `v0.26.0`
237+
mod paritydb_0_26_0 {
238+
use parity_db::{CompressionType, Db, Options};
239+
use std::path::PathBuf;
240+
use strum::{Display, EnumIter, IntoEnumIterator};
241+
242+
#[derive(Copy, Clone, Debug, PartialEq, EnumIter, Display)]
243+
#[repr(u8)]
244+
pub(super) enum DbColumn {
245+
GraphDagCborBlake2b256,
246+
GraphFull,
247+
Settings,
248+
EthMappings,
249+
PersistentGraph,
250+
Indices,
251+
}
252+
253+
impl DbColumn {
254+
fn create_column_options(compression: CompressionType) -> Vec<parity_db::ColumnOptions> {
255+
DbColumn::iter()
256+
.map(|col| {
257+
match col {
258+
DbColumn::GraphDagCborBlake2b256 | DbColumn::PersistentGraph => {
259+
parity_db::ColumnOptions {
260+
preimage: true,
261+
compression,
262+
..Default::default()
263+
}
264+
}
265+
DbColumn::GraphFull => parity_db::ColumnOptions {
266+
preimage: true,
267+
// This is needed for key retrieval.
268+
btree_index: true,
269+
compression,
270+
..Default::default()
271+
},
272+
DbColumn::Settings => {
273+
parity_db::ColumnOptions {
274+
// explicitly disable preimage for settings column
275+
// othewise we are not able to overwrite entries
276+
preimage: false,
277+
// This is needed for key retrieval.
278+
btree_index: true,
279+
compression,
280+
..Default::default()
281+
}
282+
}
283+
DbColumn::EthMappings => parity_db::ColumnOptions {
284+
preimage: false,
285+
btree_index: false,
286+
compression,
287+
..Default::default()
288+
},
289+
DbColumn::Indices => parity_db::ColumnOptions {
290+
preimage: false,
291+
btree_index: false,
292+
compression,
293+
..Default::default()
294+
},
295+
}
296+
})
297+
.collect()
298+
}
299+
}
300+
301+
pub(super) struct ParityDb {
302+
pub db: parity_db::Db,
303+
}
304+
305+
impl ParityDb {
306+
pub(super) fn to_options(path: PathBuf) -> Options {
307+
Options {
308+
path,
309+
sync_wal: true,
310+
sync_data: true,
311+
stats: false,
312+
salt: None,
313+
columns: DbColumn::create_column_options(CompressionType::Lz4),
314+
compression_threshold: [(0, 128)].into_iter().collect(),
315+
}
316+
}
317+
318+
pub(super) fn open(path: impl Into<PathBuf>) -> anyhow::Result<Self> {
319+
let opts = Self::to_options(path.into());
320+
Ok(Self {
321+
db: Db::open_or_create(&opts)?,
322+
})
323+
}
324+
}
325+
}

0 commit comments

Comments
 (0)