pallet_msa/migration/
v2.rs

1use crate::{migration::v1, Config, Pallet, ProviderToRegistryEntryV2};
2pub use alloc::vec;
3use common_primitives::msa::{ProviderId, ProviderRegistryEntry};
4use frame_support::{pallet_prelude::*, storage_alias, weights::Weight};
5pub use frame_system::pallet_prelude::BlockNumberFor;
6#[cfg(feature = "try-runtime")]
7pub use sp_runtime::TryRuntimeError;
8
9const LOG_TARGET: &str = "runtime::provider";
10pub const MAX_ITEMS_PER_BLOCK: u32 = 50; // Conservative batch size for Paseo
11
12/// Storage item to track migration progress for Paseo
13#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
14pub struct MigrationStatus {
15	/// Number of items migrated so far
16	pub migrated_count: u32,
17	/// Whether migration is complete
18	pub completed: bool,
19}
20
21/// Storage for tracking migration progress
22#[storage_alias]
23pub type MigrationProgressV2<T: Config> = StorageValue<Pallet<T>, MigrationStatus, ValueQuery>;
24
25/// Core migration function that can be called from both contexts
26pub fn migrate_provider_entries_batch<T: Config>(batch_size: usize) -> (Weight, u32) {
27	let mut reads = 0u64;
28	let mut writes = 0u64;
29	let mut bytes = 0u64;
30	let mut migrated_count = 0u32;
31
32	let entries: vec::Vec<(
33		ProviderId,
34		v1::ProviderRegistryEntry<<T as Config>::MaxProviderNameSize>,
35	)> = v1::ProviderToRegistryEntry::<T>::drain().take(batch_size).collect();
36
37	for (provider_id, old_entry) in entries {
38		// Build new registry entry with old provider name
39		let migrated_provider_entry = ProviderRegistryEntry {
40			default_name: old_entry.provider_name.clone(),
41			default_logo_250_100_png_cid: BoundedVec::default(),
42			localized_logo_250_100_png_cids: BoundedBTreeMap::default(),
43			localized_names: BoundedBTreeMap::default(),
44		};
45		// Insert into new storage
46		ProviderToRegistryEntryV2::<T>::insert(provider_id, migrated_provider_entry);
47		reads += 1;
48		writes += 1;
49		bytes += old_entry.encoded_size() as u64;
50		migrated_count += 1;
51	}
52
53	let weight = T::DbWeight::get().reads_writes(reads, writes).add_proof_size(bytes);
54	(weight, migrated_count)
55}
56
57/// Hook-based multi-block migration for Frequency Provider Migration
58/// This should be called from your pallet's on_initialize hook
59pub fn on_initialize_migration<T: Config>() -> Weight {
60	// Check if migration is needed
61	let onchain_version = Pallet::<T>::on_chain_storage_version();
62	if onchain_version >= 2 {
63		// Clean up any leftover migration state
64		if MigrationProgressV2::<T>::exists() {
65			MigrationProgressV2::<T>::kill();
66			return T::DbWeight::get().reads_writes(1, 1)
67		}
68		return T::DbWeight::get().reads(1)
69	}
70
71	let migration_status = MigrationProgressV2::<T>::get();
72	let mut reads = 1u64;
73	let mut writes = 0u64;
74
75	// Initialize migration if not started
76	if !migration_status.completed {
77		let (batch_weight, migrated_in_this_block) =
78			migrate_provider_entries_batch::<T>(MAX_ITEMS_PER_BLOCK as usize);
79
80		let mut updated_status = migration_status;
81		updated_status.migrated_count += migrated_in_this_block;
82		updated_status.completed = migrated_in_this_block == 0;
83		reads += 1;
84		// Check if migration is complete
85		if updated_status.completed {
86			StorageVersion::new(2).put::<Pallet<T>>();
87			MigrationProgressV2::<T>::kill();
88			writes += 2;
89			log::info!(target: LOG_TARGET, "Hook-based migration completed! Total migrated: {}", updated_status.migrated_count);
90		} else {
91			MigrationProgressV2::<T>::put(&updated_status);
92			writes += 1;
93		}
94		return T::DbWeight::get().reads_writes(reads, writes).saturating_add(batch_weight);
95	}
96	log::info!(target: LOG_TARGET, "Provider Registry Migration already completed.");
97	T::DbWeight::get().reads(1)
98}