mononoke: don't use current version for bookmark renamer

Summary:
Use parameters added in D32018280 to avoid using current version to generate
bookmark renamer

Reviewed By: mitrandir77

Differential Revision: D32023697

fbshipit-source-id: 5256bd625ee028325f03d485f59bed47ec390365
This commit is contained in:
Stanislau Hlebik 2021-11-02 11:44:53 -07:00 committed by Facebook GitHub Bot
parent 9156bc011f
commit 039b67d0c2
19 changed files with 403 additions and 212 deletions

View File

@ -1,4 +1,4 @@
// @generated SignedSource<<cf4de91d14ad9f4e1ace66d355c448f3>>
// @generated SignedSource<<a23bb7cd6954e30dfa0b9d23ad49e5fc>>
// DO NOT EDIT THIS FILE MANUALLY!
// This file is a mechanical copy of the version in the configerator repo. To
// modify it, edit the copy in the configerator repo instead and copy it over by
@ -19,11 +19,32 @@ namespace py configerator.mononoke.commitsync
typedef string LargeRepoName
/// Config that doesn't change from version to version
struct RawSmallRepoPermanentConfig {
/// Bookmark prefix to use for each small repo bookmark
/// when it's remapped to a large repo (except for common_pushrebase_bookmarks)
1: string bookmark_prefix,
} (rust.exhaustive)
/// Config that applies for all versions for a given large repo
struct CommonCommitSyncConfig {
/// Mapping from small repos id to their parameters
1: map<i32, RawSmallRepoPermanentConfig> small_repos,
/// Bookmarks that have the same name in small and large repos
/// and that are possible to pushrebase to from small repos.
2: list<string> common_pushrebase_bookmarks,
/// Id of the large repo
3: i32 large_repo_id,
} (rust.exhaustive)
struct RawCommitSyncConfigAllVersionsOneRepo {
/// All versions of `RawCommitSyncConfig` ever present for a given repo
1: list<repos.RawCommitSyncConfig> versions,
/// Current version of `RawCommitSyncConfig` used by a given repo
/// DEPRECATED, WILL BE DELETED SOON
2: string current_version,
/// Common config that applies to all versions
3: CommonCommitSyncConfig common,
} (rust.exhaustive)
struct RawCommitSyncAllVersions {

View File

@ -17,6 +17,7 @@ use cmdlib::{
args::{self, MononokeMatches},
helpers,
};
use commitsync::types::CommonCommitSyncConfig;
use context::CoreContext;
use cross_repo_sync::{
create_commit_syncer_lease,
@ -29,7 +30,7 @@ use futures::{compat::Future01CompatExt, stream, try_join, TryFutureExt};
use itertools::Itertools;
use live_commit_sync_config::{CfgrLiveCommitSyncConfig, LiveCommitSyncConfig};
use maplit::{btreemap, hashmap, hashset};
use metaconfig_types::{BookmarkAttrs, CommitSyncConfig, RepoConfig};
use metaconfig_types::{BookmarkAttrs, CommitSyncConfig};
use metaconfig_types::{CommitSyncConfigVersion, DefaultSmallToLargeCommitSyncPathAction};
use mononoke_types::{
BonsaiChangesetMut, ChangesetId, DateTime, FileChange, FileType, MPath, RepositoryId,
@ -146,21 +147,14 @@ pub async fn subcommand_crossrepo<'a>(
.map_err(|e| e.into())
}
(VERIFY_BOOKMARKS_SUBCOMMAND, Some(sub_sub_m)) => {
let config_store = matches.config_store();
let (source_repo, target_repo, mapping) =
get_source_target_repos_and_mapping(fb, logger, matches).await?;
let source_repo_id = source_repo.get_repoid();
let (_, source_repo_config) =
args::get_config_by_repoid(config_store, matches, source_repo_id)?;
let update_large_repo_bookmarks = sub_sub_m.is_present(UPDATE_LARGE_REPO_BOOKMARKS);
subcommand_verify_bookmarks(
ctx,
source_repo,
source_repo_config,
target_repo,
mapping,
update_large_repo_bookmarks,
@ -1012,13 +1006,15 @@ async fn subcommand_map(
async fn subcommand_verify_bookmarks(
ctx: CoreContext,
source_repo: BlobRepo,
source_repo_config: RepoConfig,
target_repo: BlobRepo,
mapping: SqlSyncedCommitMapping,
should_update_large_repo_bookmarks: bool,
live_commit_sync_config: Arc<dyn LiveCommitSyncConfig>,
matches: &MononokeMatches<'_>,
) -> Result<(), SubcommandError> {
let common_config = live_commit_sync_config
.get_common_config(target_repo.get_repoid())
.await?;
let commit_syncer = get_large_to_small_commit_syncer(
&ctx,
source_repo,
@ -1038,15 +1034,11 @@ async fn subcommand_verify_bookmarks(
Ok(())
} else {
if should_update_large_repo_bookmarks {
let commit_sync_config = source_repo_config
.commit_sync_config
.as_ref()
.ok_or_else(|| format_err!("missing CommitSyncMapping config"))?;
update_large_repo_bookmarks(
ctx.clone(),
&diff,
small_repo,
commit_sync_config,
&common_config,
large_repo,
mapping,
)
@ -1109,7 +1101,7 @@ async fn update_large_repo_bookmarks(
ctx: CoreContext,
diff: &Vec<BookmarkDiff>,
small_repo: &BlobRepo,
commit_sync_config: &CommitSyncConfig,
common_commit_sync_config: &CommonCommitSyncConfig,
large_repo: &BlobRepo,
mapping: SqlSyncedCommitMapping,
) -> Result<(), Error> {
@ -1120,11 +1112,12 @@ async fn update_large_repo_bookmarks(
);
let mut book_txn = large_repo.update_bookmark_transaction(ctx.clone());
let bookmark_renamer = get_small_to_large_renamer(commit_sync_config, small_repo.get_repoid())?;
let bookmark_renamer =
get_small_to_large_renamer(common_commit_sync_config, small_repo.get_repoid())?;
for d in diff {
if commit_sync_config
if common_commit_sync_config
.common_pushrebase_bookmarks
.contains(d.target_bookmark())
.contains(&d.target_bookmark().to_string())
{
info!(
ctx.logger(),
@ -1460,6 +1453,7 @@ async fn get_large_to_small_commit_syncer<'a>(
mod test {
use super::*;
use bookmarks::BookmarkName;
use commitsync::types::RawSmallRepoPermanentConfig;
use cross_repo_sync::{
types::{Source, Target},
validation::find_bookmark_diff,
@ -1468,10 +1462,7 @@ mod test {
use fixtures::{linear, set_bookmark};
use futures::{compat::Stream01CompatExt, TryStreamExt};
use maplit::{hashmap, hashset};
use metaconfig_types::{
CommitSyncConfig, CommitSyncConfigVersion, CommitSyncDirection,
DefaultSmallToLargeCommitSyncPathAction, SmallRepoCommitSyncConfig,
};
use metaconfig_types::{CommitSyncConfigVersion, CommitSyncDirection};
use mononoke_types::{MPath, RepositoryId};
use revset::AncestorsNodeStream;
use sql_construct::SqlConstruct;
@ -1549,25 +1540,21 @@ mod test {
// Update the bookmarks
{
let small_repo_sync_config = SmallRepoCommitSyncConfig {
default_action: DefaultSmallToLargeCommitSyncPathAction::Preserve,
direction: CommitSyncDirection::SmallToLarge,
map: Default::default(),
bookmark_prefix: Default::default(),
};
let mut commit_sync_config = CommitSyncConfig {
large_repo_id: large_repo.get_repoid(),
common_pushrebase_bookmarks: vec![master.clone()],
small_repos: hashmap! {
small_repo.get_repoid() => small_repo_sync_config,
let mut common_config = CommonCommitSyncConfig {
common_pushrebase_bookmarks: vec![master.to_string()],
small_repos: btreemap! {
small_repo.get_repoid().id() => RawSmallRepoPermanentConfig {
bookmark_prefix: Default::default()
},
},
version_name: CommitSyncConfigVersion("TEST_VERSION_NAME".to_string()),
large_repo_id: large_repo.get_repoid().id(),
};
update_large_repo_bookmarks(
ctx.clone(),
&actual_diff,
small_repo,
&commit_sync_config,
&common_config,
large_repo,
commit_syncer.get_mapping().clone(),
)
@ -1590,13 +1577,13 @@ mod test {
// Now remove master bookmark from common_pushrebase_bookmarks and update large repo
// bookmarks again
commit_sync_config.common_pushrebase_bookmarks = vec![];
common_config.common_pushrebase_bookmarks = vec![];
update_large_repo_bookmarks(
ctx.clone(),
&actual_diff,
small_repo,
&commit_sync_config,
&common_config,
large_repo,
commit_syncer.get_mapping().clone(),
)
@ -1664,11 +1651,11 @@ mod test {
current_version => SyncData {
mover: Arc::new(identity_mover),
reverse_mover: Arc::new(identity_mover),
bookmark_renamer: Arc::new(noop_book_renamer),
reverse_bookmark_renamer: Arc::new(noop_book_renamer),
}
},
vec![BookmarkName::new("master")?],
Arc::new(noop_book_renamer),
Arc::new(noop_book_renamer),
);
Ok(CommitSyncer::new_with_provider(
&ctx,

View File

@ -415,17 +415,15 @@ async fn backsync_change_mapping(fb: FacebookInit) -> Result<(), Error> {
current_version.clone() => SyncData {
mover: current_mover_type.get_mover(),
reverse_mover: current_mover_type.get_reverse_mover(),
bookmark_renamer: bookmark_renamer_type.get_bookmark_renamer(),
reverse_bookmark_renamer: bookmark_renamer_type.get_reverse_bookmark_renamer(),
},
new_version.clone() => SyncData{
mover: new_mover_type.get_mover(),
reverse_mover: new_mover_type.get_reverse_mover(),
bookmark_renamer: bookmark_renamer_type.get_bookmark_renamer(),
reverse_bookmark_renamer: bookmark_renamer_type.get_reverse_bookmark_renamer(),
}
},
vec![BookmarkName::new("master")?],
bookmark_renamer_type.get_bookmark_renamer(),
bookmark_renamer_type.get_reverse_bookmark_renamer(),
);
let commit_syncer =
CommitSyncer::new_with_provider(&ctx, mapping.clone(), repos, commit_sync_data_provider);
@ -1023,11 +1021,11 @@ async fn init_repos(
current_version => SyncData {
mover: mover_type.get_mover(),
reverse_mover: mover_type.get_reverse_mover(),
bookmark_renamer: bookmark_renamer_type.get_bookmark_renamer(),
reverse_bookmark_renamer: bookmark_renamer_type.get_reverse_bookmark_renamer(),
}
},
vec![BookmarkName::new("master")?],
bookmark_renamer_type.get_bookmark_renamer(),
bookmark_renamer_type.get_reverse_bookmark_renamer(),
);
let commit_syncer =
CommitSyncer::new_with_provider(&ctx, mapping.clone(), repos, commit_sync_data_provider);
@ -1347,17 +1345,15 @@ async fn init_merged_repos(
current_version => SyncData {
mover: mover_type.get_reverse_mover(),
reverse_mover: mover_type.get_mover(),
bookmark_renamer: bookmark_renamer.clone(),
reverse_bookmark_renamer: reverse_bookmark_renamer.clone(),
},
noop_version => SyncData {
mover: Arc::new(identity_mover),
reverse_mover: Arc::new(identity_mover),
bookmark_renamer: bookmark_renamer,
reverse_bookmark_renamer: reverse_bookmark_renamer,
}
},
vec![BookmarkName::new("master")?],
bookmark_renamer.clone(),
reverse_bookmark_renamer.clone(),
);
let commit_syncer = CommitSyncer::new_with_provider(
@ -1620,11 +1616,11 @@ async fn preserve_premerge_commit(
noop_version => SyncData {
mover: Arc::new(identity_mover),
reverse_mover: Arc::new(identity_mover),
bookmark_renamer: bookmark_renamer.clone(),
reverse_bookmark_renamer: bookmark_renamer.clone(),
}
},
vec![BookmarkName::new("master")?],
bookmark_renamer.clone(),
bookmark_renamer.clone(),
);
CommitSyncer::new_with_provider(&ctx, mapping.clone(), repos, commit_sync_data_provider)
};

View File

@ -10,9 +10,11 @@
use anyhow::Result;
use ascii::AsciiString;
use bookmarks::BookmarkName;
use metaconfig_types::{CommitSyncConfig, CommitSyncDirection};
use commitsync::types::CommonCommitSyncConfig;
use metaconfig_types::CommitSyncDirection;
use mononoke_types::RepositoryId;
use std::collections::HashSet;
use std::str::FromStr;
use std::sync::Arc;
use thiserror::Error;
@ -33,26 +35,28 @@ pub struct BookmarkRenamers {
}
fn get_prefix_and_common_bookmarks(
commit_sync_config: &CommitSyncConfig,
commit_sync_config: &CommonCommitSyncConfig,
small_repo_id: RepositoryId,
) -> Result<(AsciiString, HashSet<BookmarkName>)> {
let common_pushrebase_bookmarks: HashSet<BookmarkName> = commit_sync_config
let common_pushrebase_bookmarks: Result<HashSet<BookmarkName>> = commit_sync_config
.common_pushrebase_bookmarks
.iter()
.cloned()
.map(BookmarkName::new)
.collect();
let common_pushrebase_bookmarks = common_pushrebase_bookmarks?;
let prefix = commit_sync_config
.small_repos
.get(&small_repo_id)
.get(&small_repo_id.id())
.ok_or(ErrorKind::SmallRepoNotFound(small_repo_id))?
.bookmark_prefix
.clone();
Ok((prefix, common_pushrebase_bookmarks))
Ok((AsciiString::from_str(&prefix)?, common_pushrebase_bookmarks))
}
/// Get a renamer for small-to-large repo sync
pub fn get_small_to_large_renamer(
commit_sync_config: &CommitSyncConfig,
commit_sync_config: &CommonCommitSyncConfig,
small_repo_id: RepositoryId,
) -> Result<BookmarkRenamer> {
let (prefix, common_pushrebase_bookmarks) =
@ -70,7 +74,7 @@ pub fn get_small_to_large_renamer(
/// Get a renamer for a large-to-small repo sync
pub fn get_large_to_small_renamer(
commit_sync_config: &CommitSyncConfig,
commit_sync_config: &CommonCommitSyncConfig,
small_repo_id: RepositoryId,
) -> Result<BookmarkRenamer> {
let (prefix, common_pushrebase_bookmarks) =
@ -90,7 +94,7 @@ pub fn get_large_to_small_renamer(
/// Get both forward and reverse bookmark renamer in the `BookmarkRenamers` struct
pub fn get_bookmark_renamers(
commit_sync_config: &CommitSyncConfig,
commit_sync_config: &CommonCommitSyncConfig,
small_repo_id: RepositoryId,
direction: CommitSyncDirection,
) -> Result<BookmarkRenamers> {
@ -115,46 +119,21 @@ pub fn get_bookmark_renamers(
#[cfg(test)]
mod test {
use super::*;
use maplit::hashmap;
use mercurial_types::MPath;
use metaconfig_types::{
CommitSyncConfigVersion, DefaultSmallToLargeCommitSyncPathAction, SmallRepoCommitSyncConfig,
};
use commitsync::types::RawSmallRepoPermanentConfig;
use maplit::btreemap;
fn mp(s: &'static str) -> MPath {
MPath::new(s).unwrap()
}
fn get_small_repo_sync_config_1() -> SmallRepoCommitSyncConfig {
SmallRepoCommitSyncConfig {
default_action: DefaultSmallToLargeCommitSyncPathAction::Preserve,
map: hashmap! {},
bookmark_prefix: AsciiString::from_ascii("b1/".to_string()).unwrap(),
direction: CommitSyncDirection::LargeToSmall,
}
}
fn get_small_repo_sync_config_2() -> SmallRepoCommitSyncConfig {
SmallRepoCommitSyncConfig {
default_action: DefaultSmallToLargeCommitSyncPathAction::PrependPrefix(mp("shifted")),
map: hashmap! {},
bookmark_prefix: AsciiString::from_ascii("b2/".to_string()).unwrap(),
direction: CommitSyncDirection::LargeToSmall,
}
}
fn get_commit_sync_config() -> CommitSyncConfig {
CommitSyncConfig {
large_repo_id: RepositoryId::new(3),
common_pushrebase_bookmarks: vec![
BookmarkName::new("m1").unwrap(),
BookmarkName::new("m2").unwrap(),
],
small_repos: hashmap! {
RepositoryId::new(1) => get_small_repo_sync_config_1(),
RepositoryId::new(2) => get_small_repo_sync_config_2(),
fn get_commit_sync_config() -> CommonCommitSyncConfig {
CommonCommitSyncConfig {
common_pushrebase_bookmarks: vec!["m1".to_string(), "m2".to_string()],
small_repos: btreemap! {
1 => RawSmallRepoPermanentConfig {
bookmark_prefix: "b1/".to_string()
},
2 => RawSmallRepoPermanentConfig {
bookmark_prefix: "b2/".to_string()
},
},
version_name: CommitSyncConfigVersion("TEST_VERSION_NAME".to_string()),
large_repo_id: 0,
}
}

View File

@ -8,7 +8,9 @@
use anyhow::{anyhow, Error};
use bookmark_renaming::{get_bookmark_renamers, BookmarkRenamer, BookmarkRenamers};
use bookmarks::BookmarkName;
use commitsync::types::CommonCommitSyncConfig;
use context::CoreContext;
use futures::future::try_join;
use live_commit_sync_config::LiveCommitSyncConfig;
use metaconfig_types::{CommitSyncConfig, CommitSyncConfigVersion, CommitSyncDirection};
use mononoke_types::RepositoryId;
@ -27,6 +29,8 @@ pub enum CommitSyncDataProvider {
source_repo_id: RepositoryId,
target_repo_id: RepositoryId,
common_pushrebase_bookmarks: Vec<BookmarkName>,
bookmark_renamer: BookmarkRenamer,
reverse_bookmark_renamer: BookmarkRenamer,
},
}
@ -34,8 +38,6 @@ pub enum CommitSyncDataProvider {
pub struct SyncData {
pub mover: Mover,
pub reverse_mover: Mover,
pub bookmark_renamer: BookmarkRenamer,
pub reverse_bookmark_renamer: BookmarkRenamer,
}
impl SyncData {
@ -43,15 +45,11 @@ impl SyncData {
let Self {
mover,
reverse_mover,
bookmark_renamer,
reverse_bookmark_renamer,
} = self;
Self {
mover: reverse_mover,
reverse_mover: mover,
bookmark_renamer: reverse_bookmark_renamer,
reverse_bookmark_renamer: bookmark_renamer,
}
}
}
@ -63,6 +61,8 @@ impl CommitSyncDataProvider {
target_repo_id: Target<RepositoryId>,
map: HashMap<CommitSyncConfigVersion, SyncData>,
common_pushrebase_bookmarks: Vec<BookmarkName>,
bookmark_renamer: BookmarkRenamer,
reverse_bookmark_renamer: BookmarkRenamer,
) -> Self {
Self::Test {
current_version: Arc::new(Mutex::new(current_version)),
@ -70,6 +70,8 @@ impl CommitSyncDataProvider {
source_repo_id: source_repo_id.0,
target_repo_id: target_repo_id.0,
common_pushrebase_bookmarks,
bookmark_renamer,
reverse_bookmark_renamer,
}
}
@ -123,11 +125,18 @@ impl CommitSyncDataProvider {
match self {
Live(live_commit_sync_config) => {
let commit_sync_config = live_commit_sync_config
.get_commit_sync_config_by_version(source_repo_id, version)
.await?;
.get_commit_sync_config_by_version(source_repo_id, version);
let common_config = live_commit_sync_config.get_common_config(source_repo_id);
let Movers { mover, .. } =
get_movers_from_config(&commit_sync_config, source_repo_id, target_repo_id)?;
let (commit_sync_config, common_config) =
try_join(commit_sync_config, common_config).await?;
let Movers { mover, .. } = get_movers_from_config(
&common_config,
&commit_sync_config,
source_repo_id,
target_repo_id,
)?;
Ok(mover)
}
Test { .. } => {
@ -148,11 +157,18 @@ impl CommitSyncDataProvider {
match self {
Live(live_commit_sync_config) => {
let commit_sync_config = live_commit_sync_config
.get_commit_sync_config_by_version(source_repo_id, version)
.await?;
.get_commit_sync_config_by_version(source_repo_id, version);
let common_config = live_commit_sync_config.get_common_config(source_repo_id);
let Movers { reverse_mover, .. } =
get_movers_from_config(&commit_sync_config, source_repo_id, target_repo_id)?;
let (commit_sync_config, common_config) =
try_join(commit_sync_config, common_config).await?;
let Movers { reverse_mover, .. } = get_movers_from_config(
&common_config,
&commit_sync_config,
source_repo_id,
target_repo_id,
)?;
Ok(reverse_mover)
}
Test { .. } => {
@ -164,7 +180,6 @@ impl CommitSyncDataProvider {
pub async fn get_bookmark_renamer(
&self,
version: &CommitSyncConfigVersion,
source_repo_id: RepositoryId,
target_repo_id: RepositoryId,
) -> Result<BookmarkRenamer, Error> {
@ -173,7 +188,7 @@ impl CommitSyncDataProvider {
match self {
Live(live_commit_sync_config) => {
let commit_sync_config = live_commit_sync_config
.get_commit_sync_config_by_version(source_repo_id, version)
.get_common_config(source_repo_id)
.await?;
let BookmarkRenamers {
@ -185,16 +200,14 @@ impl CommitSyncDataProvider {
)?;
Ok(bookmark_renamer)
}
Test { .. } => {
let sync_data = self.get_sync_data(version, source_repo_id, target_repo_id)?;
Ok(sync_data.bookmark_renamer)
}
Test {
bookmark_renamer, ..
} => Ok(bookmark_renamer.clone()),
}
}
pub async fn get_reverse_bookmark_renamer(
&self,
version: &CommitSyncConfigVersion,
source_repo_id: RepositoryId,
target_repo_id: RepositoryId,
) -> Result<BookmarkRenamer, Error> {
@ -203,7 +216,7 @@ impl CommitSyncDataProvider {
match self {
Live(live_commit_sync_config) => {
let commit_sync_config = live_commit_sync_config
.get_commit_sync_config_by_version(source_repo_id, version)
.get_common_config(source_repo_id)
.await?;
let BookmarkRenamers {
@ -216,10 +229,10 @@ impl CommitSyncDataProvider {
)?;
Ok(reverse_bookmark_renamer)
}
Test { .. } => {
let sync_data = self.get_sync_data(version, source_repo_id, target_repo_id)?;
Ok(sync_data.reverse_bookmark_renamer)
}
Test {
reverse_bookmark_renamer,
..
} => Ok(reverse_bookmark_renamer.clone()),
}
}
@ -295,36 +308,37 @@ impl CommitSyncDataProvider {
}
fn get_movers_from_config(
common_config: &CommonCommitSyncConfig,
commit_sync_config: &CommitSyncConfig,
source_repo_id: RepositoryId,
target_repo_id: RepositoryId,
) -> Result<Movers, Error> {
let (direction, small_repo_id) =
get_direction_and_small_repo_id(commit_sync_config, source_repo_id, target_repo_id)?;
get_direction_and_small_repo_id(common_config, source_repo_id, target_repo_id)?;
get_movers(&commit_sync_config, small_repo_id, direction)
}
fn get_bookmark_renamers_from_config(
commit_sync_config: &CommitSyncConfig,
common_config: &CommonCommitSyncConfig,
source_repo_id: RepositoryId,
target_repo_id: RepositoryId,
) -> Result<BookmarkRenamers, Error> {
let (direction, small_repo_id) =
get_direction_and_small_repo_id(commit_sync_config, source_repo_id, target_repo_id)?;
get_bookmark_renamers(commit_sync_config, small_repo_id, direction)
get_direction_and_small_repo_id(common_config, source_repo_id, target_repo_id)?;
get_bookmark_renamers(common_config, small_repo_id, direction)
}
fn get_direction_and_small_repo_id(
commit_sync_config: &CommitSyncConfig,
common_config: &CommonCommitSyncConfig,
source_repo_id: RepositoryId,
target_repo_id: RepositoryId,
) -> Result<(CommitSyncDirection, RepositoryId), Error> {
let small_repo_id = if commit_sync_config.large_repo_id == source_repo_id
&& commit_sync_config.small_repos.contains_key(&target_repo_id)
let small_repo_id = if common_config.large_repo_id == source_repo_id.id()
&& common_config.small_repos.contains_key(&target_repo_id.id())
{
target_repo_id
} else if commit_sync_config.large_repo_id == target_repo_id
&& commit_sync_config.small_repos.contains_key(&source_repo_id)
} else if common_config.large_repo_id == target_repo_id.id()
&& common_config.small_repos.contains_key(&source_repo_id.id())
{
source_repo_id
} else {

View File

@ -506,14 +506,10 @@ where
}
pub async fn get_bookmark_renamer(&self, ctx: &CoreContext) -> Result<BookmarkRenamer, Error> {
let (source_repo, target_repo, version_name) = self.get_source_target_version(ctx).await?;
let (source_repo, target_repo, _) = self.get_source_target_version(ctx).await?;
self.commit_sync_data_provider
.get_bookmark_renamer(
&version_name,
source_repo.get_repoid(),
target_repo.get_repoid(),
)
.get_bookmark_renamer(source_repo.get_repoid(), target_repo.get_repoid())
.await
}
@ -521,14 +517,10 @@ where
&self,
ctx: &CoreContext,
) -> Result<BookmarkRenamer, Error> {
let (source_repo, target_repo, version_name) = self.get_source_target_version(ctx).await?;
let (source_repo, target_repo, _) = self.get_source_target_version(ctx).await?;
self.commit_sync_data_provider
.get_reverse_bookmark_renamer(
&version_name,
source_repo.get_repoid(),
target_repo.get_repoid(),
)
.get_reverse_bookmark_renamer(source_repo.get_repoid(), target_repo.get_repoid())
.await
}

View File

@ -1213,11 +1213,11 @@ mod test {
current_version => SyncData {
mover: Arc::new(identity_mover),
reverse_mover: Arc::new(identity_mover),
bookmark_renamer: bookmark_renamer,
reverse_bookmark_renamer: reverse_bookmark_renamer,
}
},
vec![BookmarkName::new("master")?],
bookmark_renamer,
reverse_bookmark_renamer,
);
Ok(CommitSyncer::new_with_provider(

View File

@ -24,6 +24,7 @@ use blobstore::{Loadable, Storable};
use bookmarks::{BookmarkName, BookmarkUpdateReason};
use cacheblob::InProcessLease;
use cloned::cloned;
use commitsync::types::{CommonCommitSyncConfig, RawSmallRepoPermanentConfig};
use context::CoreContext;
use cross_repo_sync::{
update_mapping_with_version, validation::verify_working_copy, CommitSyncContext,
@ -311,11 +312,20 @@ fn create_small_to_large_commit_syncer(
let large_repo_id = large_repo.get_repoid();
let commit_sync_config = create_commit_sync_config(small_repo_id, large_repo_id, prefix)?;
let repos = CommitSyncRepos::new(small_repo, large_repo, &commit_sync_config)?;
let repos = CommitSyncRepos::new(small_repo.clone(), large_repo.clone(), &commit_sync_config)?;
let (sync_config, source) = TestLiveCommitSyncConfig::new_with_source();
source.add_config(commit_sync_config.clone());
source.add_current_version(commit_sync_config.version_name);
source.add_common_config(CommonCommitSyncConfig {
common_pushrebase_bookmarks: vec![],
small_repos: btreemap! {
small_repo.get_repoid().id() => RawSmallRepoPermanentConfig {
bookmark_prefix: "".to_string(),
}
},
large_repo_id: large_repo.get_repoid().id(),
});
let live_commit_sync_config = Arc::new(sync_config);
let lease = Arc::new(InProcessLease::new());
@ -345,11 +355,20 @@ fn create_large_to_small_commit_syncer_and_config_source(
let large_repo_id = large_repo.get_repoid();
let commit_sync_config = create_commit_sync_config(small_repo_id, large_repo_id, prefix)?;
let repos = CommitSyncRepos::new(large_repo, small_repo, &commit_sync_config)?;
let repos = CommitSyncRepos::new(large_repo.clone(), small_repo.clone(), &commit_sync_config)?;
let (sync_config, source) = TestLiveCommitSyncConfig::new_with_source();
source.add_config(commit_sync_config.clone());
source.add_current_version(commit_sync_config.version_name);
source.add_common_config(CommonCommitSyncConfig {
common_pushrebase_bookmarks: vec![],
small_repos: btreemap! {
small_repo.get_repoid().id() => RawSmallRepoPermanentConfig {
bookmark_prefix: "".to_string(),
}
},
large_repo_id: large_repo.get_repoid().id(),
});
let live_commit_sync_config = Arc::new(sync_config);
let lease = Arc::new(InProcessLease::new());
@ -800,11 +819,11 @@ async fn test_sync_remap_failure(fb: FacebookInit) -> Result<(), Error> {
current_version.clone() => SyncData {
mover: Arc::new(move |_path: &MPath| bail!("This always fails")),
reverse_mover: Arc::new(move |_path: &MPath| bail!("This always fails")),
bookmark_renamer: Arc::new(identity_renamer),
reverse_bookmark_renamer: Arc::new(identity_renamer),
}
},
vec![BookmarkName::new("master")?],
Arc::new(identity_renamer),
Arc::new(identity_renamer),
);
fail_config.commit_sync_data_provider = commit_sync_data_provider;
@ -847,11 +866,11 @@ async fn test_sync_remap_failure(fb: FacebookInit) -> Result<(), Error> {
_ => Ok(Some(mpath("linear").join(path))),
}
}),
bookmark_renamer: Arc::new(identity_renamer),
reverse_bookmark_renamer: Arc::new(identity_renamer),
}
},
vec![BookmarkName::new("master")?],
Arc::new(identity_renamer),
Arc::new(identity_renamer),
);
copyfrom_fail_config.commit_sync_data_provider = commit_sync_data_provider;
copyfrom_fail_config.repos = copyfrom_fail_repos;
@ -958,11 +977,11 @@ async fn test_sync_implicit_deletes(fb: FacebookInit) -> Result<(), Error> {
current_version.clone() => SyncData {
mover,
reverse_mover,
bookmark_renamer: Arc::new(identity_renamer),
reverse_bookmark_renamer: Arc::new(identity_renamer),
}
},
vec![BookmarkName::new("master")?],
Arc::new(identity_renamer),
Arc::new(identity_renamer),
);
commit_syncer.commit_sync_data_provider = commit_sync_data_provider;
commit_syncer.repos = commit_sync_repos;
@ -1926,6 +1945,15 @@ async fn prepare_commit_syncer_with_mapping_change(
};
config_source.remove_current_version(&old_version);
config_source.add_current_version(new_version.clone());
config_source.add_common_config(CommonCommitSyncConfig {
common_pushrebase_bookmarks: vec![],
small_repos: btreemap! {
small_repo.get_repoid().id() => RawSmallRepoPermanentConfig {
bookmark_prefix: "".to_string(),
}
},
large_repo_id: large_repo_id.id(),
});
config_source.add_config(commit_sync_config);
// Create manual commit to change mapping
@ -1987,17 +2015,15 @@ fn get_merge_sync_data_provider(
v1 => SyncData {
mover: Arc::new(identity_mover),
reverse_mover: Arc::new(identity_mover),
bookmark_renamer: idrn.clone(),
reverse_bookmark_renamer: idrn.clone(),
},
v2 => SyncData {
mover: Arc::new(identity_mover),
reverse_mover: Arc::new(identity_mover),
bookmark_renamer: idrn.clone(),
reverse_bookmark_renamer: idrn,
}
},
vec![BookmarkName::new("master").unwrap()],
idrn.clone(),
idrn,
)
}
@ -2299,11 +2325,11 @@ async fn test_no_accidental_preserved_roots(
current_version.clone() => SyncData {
mover: Arc::new(identity_mover),
reverse_mover: Arc::new(identity_mover),
bookmark_renamer: Arc::new(identity_renamer),
reverse_bookmark_renamer: Arc::new(identity_renamer),
}
},
vec![BookmarkName::new("master")?],
Arc::new(identity_renamer),
Arc::new(identity_renamer),
);
commit_syncer.commit_sync_data_provider = commit_sync_data_provider;
commit_syncer.repos = commit_sync_repos.clone();

View File

@ -15,6 +15,7 @@ use blobrepo_hg::BlobRepoHg;
use blobstore::Loadable;
use bookmarks::{BookmarkName, BookmarkUpdateReason};
use commit_transformation::upload_commits;
use commitsync::types::{CommonCommitSyncConfig, RawSmallRepoPermanentConfig};
use context::CoreContext;
use cross_repo_sync::{
rewrite_commit,
@ -24,7 +25,7 @@ use cross_repo_sync::{
};
use futures::compat::Future01CompatExt;
use live_commit_sync_config::{LiveCommitSyncConfig, TestLiveCommitSyncConfig};
use maplit::hashmap;
use maplit::{btreemap, hashmap};
use megarepolib::{common::ChangesetArgs, perform_move};
use metaconfig_types::{
CommitSyncConfig, CommitSyncConfigVersion, CommitSyncDirection,
@ -148,17 +149,15 @@ pub async fn init_small_large_repo(
noop_version.clone() => SyncData {
mover: Arc::new(identity_mover),
reverse_mover: Arc::new(identity_mover),
bookmark_renamer: Arc::new(identity_renamer),
reverse_bookmark_renamer: Arc::new(identity_renamer),
},
current_version.clone() => SyncData {
mover: Arc::new(prefix_mover),
reverse_mover: Arc::new(reverse_prefix_mover),
bookmark_renamer: Arc::new(identity_renamer),
reverse_bookmark_renamer: Arc::new(identity_renamer),
}
},
vec![BookmarkName::new("master")?],
Arc::new(identity_renamer),
Arc::new(identity_renamer),
);
let small_to_large_commit_syncer = CommitSyncer::new_with_provider(
@ -181,17 +180,15 @@ pub async fn init_small_large_repo(
noop_version.clone() => SyncData {
mover: Arc::new(identity_mover),
reverse_mover: Arc::new(identity_mover),
bookmark_renamer: Arc::new(identity_renamer),
reverse_bookmark_renamer: Arc::new(identity_renamer),
},
current_version => SyncData {
mover: Arc::new(reverse_prefix_mover),
reverse_mover: Arc::new(prefix_mover),
bookmark_renamer: Arc::new(identity_renamer),
reverse_bookmark_renamer: Arc::new(identity_renamer),
}
},
vec![BookmarkName::new("master")?],
Arc::new(identity_renamer),
Arc::new(identity_renamer),
);
let large_to_small_commit_syncer = CommitSyncer::new_with_provider(
@ -372,6 +369,17 @@ pub fn get_live_commit_sync_config() -> Arc<dyn LiveCommitSyncConfig> {
source.add_config(first_version);
source.add_config(second_version);
source.add_common_config(CommonCommitSyncConfig {
common_pushrebase_bookmarks: vec![],
small_repos: btreemap! {
1 => RawSmallRepoPermanentConfig {
bookmark_prefix: "small".to_string(),
}
},
large_repo_id: 0,
});
Arc::new(sync_config)
}

View File

@ -10,7 +10,9 @@
use anyhow::{anyhow, Error, Result};
use async_trait::async_trait;
use cached_config::{ConfigHandle, ConfigStore};
use commitsync::types::{RawCommitSyncAllVersions, RawCommitSyncCurrentVersions};
use commitsync::types::{
CommonCommitSyncConfig, RawCommitSyncAllVersions, RawCommitSyncCurrentVersions,
};
use context::CoreContext;
use metaconfig_parser::Convert;
use metaconfig_types::{CommitSyncConfig, CommitSyncConfigVersion};
@ -41,6 +43,10 @@ pub enum ErrorKind {
PartOfMultipleCommitSyncConfigsVersionSets(RepositoryId),
#[error("{0:?} has no CommitSyncConfig with version name {1}")]
UnknownCommitSyncConfigVersion(RepositoryId, String),
#[error("{0:?} is not a part of any configs")]
NotPartOfAnyConfigs(RepositoryId),
#[error("{0:?} is a part of multiple config")]
PartOfMultipleConfigs(RepositoryId),
}
#[async_trait]
@ -118,6 +124,9 @@ pub trait LiveCommitSyncConfig: Send + Sync {
repo_id: RepositoryId,
version_name: &CommitSyncConfigVersion,
) -> Result<CommitSyncConfig>;
/// Returns a config that applies to all config versions
async fn get_common_config(&self, repo_id: RepositoryId) -> Result<CommonCommitSyncConfig>;
}
#[derive(Clone)]
@ -246,6 +255,7 @@ impl LiveCommitSyncConfig for CfgrLiveCommitSyncConfig {
let large_repo_config_version_sets = &self.config_handle_for_all_versions.get().repos;
let mut interesting_configs: Vec<_> = vec![];
for (_, config_version_set) in large_repo_config_version_sets.iter() {
for raw_commit_sync_config in config_version_set.versions.iter() {
if Self::related_to_repo(&raw_commit_sync_config, repo_id) {
@ -278,6 +288,31 @@ impl LiveCommitSyncConfig for CfgrLiveCommitSyncConfig {
ErrorKind::UnknownCommitSyncConfigVersion(repo_id, version_name.0.clone()).into()
})
}
async fn get_common_config(&self, repo_id: RepositoryId) -> Result<CommonCommitSyncConfig> {
let config = self.config_handle_for_all_versions.get();
let maybe_common_config = {
let interesting_common_configs = config
.repos
.iter()
.filter(|(_, all_versions_config)| {
all_versions_config.common.large_repo_id == repo_id.id()
|| all_versions_config
.common
.small_repos
.contains_key(&repo_id.id())
})
.map(|(_, all_versions_config)| all_versions_config.common.clone());
let mut iter = interesting_common_configs;
match (iter.next(), iter.next()) {
(None, _) => Ok(None),
(Some(config), None) => Ok(Some(config)),
(Some(_), Some(_)) => Err(ErrorKind::PartOfMultipleConfigs(repo_id)),
}?
};
maybe_common_config.ok_or_else(|| ErrorKind::NotPartOfAnyConfigs(repo_id).into())
}
}
/// Inner container for `TestLiveCommitSyncConfigSource`
@ -287,6 +322,7 @@ struct TestLiveCommitSyncConfigSourceInner {
current_versions: Mutex<HashSet<CommitSyncConfigVersion>>,
push_redirection_for_draft: Mutex<HashMap<RepositoryId, bool>>,
push_redirection_for_public: Mutex<HashMap<RepositoryId, bool>>,
common_configs: Mutex<Vec<CommonCommitSyncConfig>>,
}
/// A helper type to manage `TestLiveCommitSyncConfig` from outside
@ -308,6 +344,7 @@ impl TestLiveCommitSyncConfigSource {
current_versions: Mutex::new(HashSet::new()),
push_redirection_for_draft: Mutex::new(HashMap::new()),
push_redirection_for_public: Mutex::new(HashMap::new()),
common_configs: Mutex::new(vec![]),
}))
}
@ -351,6 +388,14 @@ impl TestLiveCommitSyncConfigSource {
.insert(repo_id, true);
}
pub fn add_common_config(&self, config: CommonCommitSyncConfig) {
self.0
.common_configs
.lock()
.expect("poisoned lock")
.push(config);
}
pub fn get_commit_sync_config_for_repo_if_exists(
&self,
repo_id: RepositoryId,
@ -464,6 +509,19 @@ impl TestLiveCommitSyncConfigSource {
}
}
fn get_common_config(&self, repo_id: RepositoryId) -> Result<CommonCommitSyncConfig> {
let common_configs = self.0.common_configs.lock().unwrap();
for config in common_configs.iter() {
if config.large_repo_id == repo_id.id()
|| config.small_repos.contains_key(&repo_id.id())
{
return Ok(config.clone());
}
}
Err(anyhow!("not found common config for {}", repo_id))
}
fn related_to_repo(commit_sync_config: &CommitSyncConfig, repo_id: RepositoryId) -> bool {
commit_sync_config.large_repo_id == repo_id
|| commit_sync_config
@ -543,4 +601,8 @@ impl LiveCommitSyncConfig for TestLiveCommitSyncConfig {
self.source
.get_commit_sync_config_by_version(repo_id, version_name)
}
async fn get_common_config(&self, repo_id: RepositoryId) -> Result<CommonCommitSyncConfig> {
self.source.get_common_config(repo_id)
}
}

View File

@ -14,6 +14,7 @@ use std::sync::Arc;
use anyhow::{anyhow, Context, Error};
pub use bookmarks::BookmarkName;
use commitsync::types::{CommonCommitSyncConfig, RawSmallRepoPermanentConfig};
use ephemeral_blobstore::BubbleId;
use futures::future;
use futures_watchdog::WatchdogExt;
@ -278,29 +279,45 @@ pub mod test_impl {
pub async fn new_test_xrepo(
ctx: CoreContext,
repos: impl IntoIterator<
Item = (
String,
BlobRepo,
CommitSyncConfig,
Arc<dyn SyncedCommitMapping>,
),
>,
small_repo: (String, BlobRepo),
large_repo: (String, BlobRepo),
commit_sync_config: CommitSyncConfig,
mapping: Arc<dyn SyncedCommitMapping>,
) -> Result<(Self, TestLiveCommitSyncConfigSource), Error> {
use futures::stream::{FuturesOrdered, TryStreamExt};
let (lv_cfg, lv_cfg_src) = TestLiveCommitSyncConfig::new_with_source();
let lv_cfg: Arc<dyn LiveCommitSyncConfig> = Arc::new(lv_cfg);
lv_cfg_src.add_config(commit_sync_config.clone());
lv_cfg_src.add_current_version(commit_sync_config.version_name);
let repos = repos
lv_cfg_src.add_common_config(CommonCommitSyncConfig {
common_pushrebase_bookmarks: commit_sync_config
.common_pushrebase_bookmarks
.iter()
.map(|b| b.to_string())
.collect(),
small_repos: commit_sync_config
.small_repos
.iter()
.map(|(repo_id, small_repo_config)| {
(
repo_id.id(),
RawSmallRepoPermanentConfig {
bookmark_prefix: small_repo_config.bookmark_prefix.to_string(),
},
)
})
.collect(),
large_repo_id: large_repo.1.get_repoid().id(),
});
let repos = vec![small_repo, large_repo]
.into_iter()
.map({
cloned!(lv_cfg_src);
move |(name, repo, commit_sync_config, synced_commit_maping)| {
cloned!(ctx, lv_cfg, lv_cfg_src);
move |(name, repo)| {
cloned!(ctx, lv_cfg, mapping);
async move {
lv_cfg_src.add_config(commit_sync_config.clone());
lv_cfg_src.add_current_version(commit_sync_config.version_name);
Repo::new_test_xrepo(ctx.clone(), repo, lv_cfg, synced_commit_maping)
Repo::new_test_xrepo(ctx.clone(), repo, lv_cfg, mapping)
.await
.map(move |repo| (name, Arc::new(repo)))
}

View File

@ -809,20 +809,16 @@ async fn init_x_repo(
let mapping: Arc<dyn SyncedCommitMapping> = Arc::new(small_to_large.get_mapping().clone());
Mononoke::new_test_xrepo(
ctx.clone(),
vec![
(
"smallrepo".to_string(),
small_to_large.get_small_repo().clone(),
commit_sync_config.clone(),
mapping.clone(),
),
(
"largerepo".to_string(),
small_to_large.get_large_repo().clone(),
commit_sync_config.clone(),
mapping.clone(),
),
],
(
"smallrepo".to_string(),
small_to_large.get_small_repo().clone(),
),
(
"largerepo".to_string(),
small_to_large.get_large_repo().clone(),
),
commit_sync_config.clone(),
mapping.clone(),
)
.await
}

View File

@ -20,6 +20,7 @@ mod tests {
use bookmarks::{BookmarkName, BookmarkUpdateReason, Freshness};
use cacheblob::InProcessLease;
use cached_config::{ConfigStore, ModificationTime, TestSource};
use commitsync::types::{CommonCommitSyncConfig, RawSmallRepoPermanentConfig};
use context::CoreContext;
use cross_repo_sync::{create_commit_syncers, CommitSyncContext};
use derived_data_manager::BonsaiDerivable;
@ -562,6 +563,28 @@ mod tests {
source.add_config(commit_sync_config.clone());
source.add_config(later_commit_sync_config);
source.add_current_version(commit_sync_config.version_name);
source.add_common_config(CommonCommitSyncConfig {
common_pushrebase_bookmarks: commit_sync_config
.common_pushrebase_bookmarks
.iter()
.map(|b| b.to_string())
.collect(),
small_repos: commit_sync_config
.small_repos
.iter()
.map(|(repo_id, small_repo_config)| {
(
repo_id.id(),
RawSmallRepoPermanentConfig {
bookmark_prefix: small_repo_config.bookmark_prefix.to_string(),
},
)
})
.collect(),
large_repo_id: commit_sync_config.large_repo_id.id(),
});
Arc::new(sync_config)
}

View File

@ -67,7 +67,19 @@
"version_name": "TEST_VERSION_NAME_OLD"
}
],
"current_version": "TEST_VERSION_NAME"
"current_version": "TEST_VERSION_NAME",
"common": {
"common_pushrebase_bookmarks": ["master_bookmark"],
"large_repo_id": 0,
"small_repos": {
"1": {
"bookmark_prefix": "fbsource/"
},
"2": {
"bookmark_prefix": "ovrsource/"
}
}
}
}
}
}

View File

@ -99,7 +99,19 @@
"version_name": "TEST_VERSION_NAME_FLIPPED"
}
],
"current_version": "TEST_VERSION_NAME"
"current_version": "TEST_VERSION_NAME",
"common": {
"common_pushrebase_bookmarks": ["master_bookmark"],
"large_repo_id": 1,
"small_repos": {
"0": {
"bookmark_prefix": "megarepo_test/"
},
"2": {
"bookmark_prefix": "ovrsource/"
}
}
}
}
}
}

View File

@ -92,7 +92,16 @@ EOF
"version_name": "test_version"
}
],
"current_version": "test_version"
"current_version": "test_version",
"common": {
"common_pushrebase_bookmarks": ["master_bookmark"],
"large_repo_id": 0,
"small_repos": {
1: {
"bookmark_prefix": "bookprefix/"
}
}
}
}
}
}
@ -230,7 +239,16 @@ EOF
"version_name": "new_version"
}
],
"current_version": "new_version"
"current_version": "new_version",
"common": {
"common_pushrebase_bookmarks": ["master_bookmark"],
"large_repo_id": 0,
"small_repos": {
1: {
"bookmark_prefix": "bookprefix/"
}
}
}
}
}
}
@ -307,7 +325,16 @@ EOF
"version_name": "TEST_VERSION_NAME_LIVE_V2"
}
],
"current_version": "TEST_VERSION_NAME_LIVE_V2"
"current_version": "TEST_VERSION_NAME_LIVE_V2",
"common": {
"common_pushrebase_bookmarks": ["master_bookmark"],
"large_repo_id": 0,
"small_repos": {
1: {
"bookmark_prefix": "bookprefix1/"
}
}
}
}
}
}

View File

@ -134,11 +134,6 @@ Perform ovrsource pushrebase, make sure it is push-redirected into Megarepo
- compare the working copies
$ REPOIDLARGE=$MEG_REPOID REPOIDSMALL=$OVR_REPOID verify_wc master_bookmark
Add a new config version to "all" configs, but do not mark it as current
This new version has fbsource as large repo. Ensure that having such version
in "all" configs does not cause any undesired effects for push-rebases
$ cp "$TEST_FIXTURES/commitsync/all_with_flipped_config.json" "$COMMIT_SYNC_CONF/all"
$ cd "$TESTTMP/ovr-hg-cnt"
$ REPONAME=ovr-mon hgmn up -q master_bookmark
$ echo 2 > pushredirected_2 && hg addremove -q && hg ci -q -m pushredirected_2
@ -170,6 +165,9 @@ but disable push-redirection until invisible merge is done
-- stop mononoke before changing config with large repo change
$ kill $MONONOKE_PID
Add a new config version to "all" configs, this new version has fbsource as large repo.
$ cp "$TEST_FIXTURES/commitsync/all_with_flipped_config.json" "$COMMIT_SYNC_CONF/all"
-- This is an expected state of our configs at the last restart before
-- the invisible merge
$ cp "$TEST_FIXTURES/commitsync/flipped_config.json" "$COMMIT_SYNC_CONF/current"

View File

@ -93,7 +93,16 @@ setup configerator configs
> "version_name": "TEST_VERSION_NAME_LIVE_V1"
> }
> ],
> "current_version": "new_version"
> "current_version": "new_version",
> "common": {
> "common_pushrebase_bookmarks": ["master_bookmark"],
> "large_repo_id": 0,
> "small_repos": {
> 1: {
> "bookmark_prefix": "bookprefix1/"
> }
> }
> }
> }
> }
> }

View File

@ -116,7 +116,19 @@ setup configerator configs
> "version_name": "TEST_VERSION_NAME_LIVE"
> }
> ],
> "current_version": "TEST_VERSION_NAME_LIVE"
> "current_version": "TEST_VERSION_NAME_LIVE",
> "common": {
> "common_pushrebase_bookmarks": ["master_bookmark"],
> "large_repo_id": 0,
> "small_repos": {
> 1: {
> "bookmark_prefix": "bookprefix1/"
> },
> 2: {
> "bookmark_prefix": "bookprefix2/"
> }
> }
> }
> }
> }
> }