pallet_msa/migration/
v2.rs

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