Skip to content

Commit

Permalink
Fix locking for version db update
Browse files Browse the repository at this point in the history
  • Loading branch information
davidanthoff committed Oct 17, 2024
1 parent fec0d15 commit 7c8ccd3
Showing 1 changed file with 119 additions and 84 deletions.
203 changes: 119 additions & 84 deletions src/operations.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::config_file::load_config_db;
use crate::config_file::load_mut_config_db;
use crate::config_file::save_config_db;
use crate::config_file::JuliaupConfig;
Expand All @@ -15,12 +16,15 @@ use crate::utils::is_valid_julia_path;
use anyhow::{anyhow, bail, Context, Result};
use bstr::ByteSlice;
use bstr::ByteVec;
use chrono::DateTime;
use chrono::Utc;
use console::style;
#[cfg(not(target_os = "freebsd"))]
use flate2::read::GzDecoder;
use indicatif::{ProgressBar, ProgressStyle};
use indoc::formatdoc;
use semver::Version;
use tempfile::TempPath;
#[cfg(not(windows))]
use std::os::unix::fs::PermissionsExt;
#[cfg(not(target_os = "freebsd"))]
Expand Down Expand Up @@ -1341,46 +1345,119 @@ mod tests {
}

pub fn update_version_db(paths: &GlobalPaths) -> Result<()> {
let mut config_file = load_mut_config_db(paths).with_context(|| {
"`run_command_update_version_db` command failed to load configuration db."
})?;
let old_last_version_db_update: Option<DateTime<Utc>>;
let direct_download_etags: Vec<(String, String)>;
let temp_versiondb_download_path: Option<TempPath>;
let mut delete_old_version_db: bool = false;

#[cfg(feature = "selfupdate")]
let juliaup_channel = match &config_file.self_data.juliaup_channel {
Some(juliaup_channel) => juliaup_channel.to_string(),
None => "release".to_string(),
};
{
let config_file = load_config_db(paths).with_context(|| {
"`run_command_update_version_db` command failed to load configuration db."
})?;

// TODO Figure out how we can learn about the correctn Juliaup channel here
#[cfg(not(feature = "selfupdate"))]
let juliaup_channel = "release".to_string();

let juliaupserver_base =
get_juliaserver_base_url().with_context(|| "Failed to get Juliaup server base URL.")?;

let dbversion_url_path = match juliaup_channel.as_str() {
"release" => "juliaup/RELEASECHANNELDBVERSION",
"releasepreview" => "juliaup/RELEASEPREVIEWCHANNELDBVERSION",
"dev" => "juliaup/DEVCHANNELDBVERSION",
_ => bail!(
"Juliaup is configured to a channel named '{}' that does not exist.",
&juliaup_channel
),
};
old_last_version_db_update = config_file.data.last_version_db_update;

let dbversion_url = juliaupserver_base
.join(dbversion_url_path)
.with_context(|| {
format!(
"Failed to construct a valid url from '{}' and '{}'.",
juliaupserver_base, dbversion_url_path
)
})?;
#[cfg(feature = "selfupdate")]
let juliaup_channel = match &config_file.self_data.juliaup_channel {
Some(juliaup_channel) => juliaup_channel.to_string(),
None => "release".to_string(),
};

// TODO Figure out how we can learn about the correctn Juliaup channel here
#[cfg(not(feature = "selfupdate"))]
let juliaup_channel = "release".to_string();

let juliaupserver_base =
get_juliaserver_base_url().with_context(|| "Failed to get Juliaup server base URL.")?;

let dbversion_url_path = match juliaup_channel.as_str() {
"release" => "juliaup/RELEASECHANNELDBVERSION",
"releasepreview" => "juliaup/RELEASEPREVIEWCHANNELDBVERSION",
"dev" => "juliaup/DEVCHANNELDBVERSION",
_ => bail!(
"Juliaup is configured to a channel named '{}' that does not exist.",
&juliaup_channel
),
};

let dbversion_url = juliaupserver_base
.join(dbversion_url_path)
.with_context(|| {
format!(
"Failed to construct a valid url from '{}' and '{}'.",
juliaupserver_base, dbversion_url_path
)
})?;

let online_dbversion = download_juliaup_version(&dbversion_url.to_string())
.with_context(|| "Failed to download current version db version.")?;

direct_download_etags = download_direct_download_etags(&config_file.data)?;

Check failure on line 1395 in src/operations.rs

View workflow job for this annotation

GitHub Actions / test-juliaup (x86_64-apple-darwin)

mismatched types

Check failure on line 1395 in src/operations.rs

View workflow job for this annotation

GitHub Actions / test-juliaup (x86_64-unknown-linux-gnu)

mismatched types

Check failure on line 1395 in src/operations.rs

View workflow job for this annotation

GitHub Actions / check-juliaup (x86_64-apple-darwin)

mismatched types

Check failure on line 1395 in src/operations.rs

View workflow job for this annotation

GitHub Actions / check-juliaup (x86_64-unknown-linux-gnu)

mismatched types

let bundled_dbversion = get_bundled_dbversion()
.with_context(|| "Failed to determine the bundled version db version.")?;

let local_dbversion = match std::fs::OpenOptions::new()
.read(true)
.open(&paths.versiondb)
{
Ok(file) => {
let reader = BufReader::new(&file);

if let Ok(versiondb) =
serde_json::from_reader::<BufReader<&std::fs::File>, JuliaupVersionDB>(reader)
{
if let Ok(version) = semver::Version::parse(&versiondb.version) {
Some(version)
} else {
None
}
} else {
None
}
}
Err(_) => None,
};

if online_dbversion > bundled_dbversion {
if local_dbversion.is_none() || online_dbversion > local_dbversion.unwrap() {
let onlineversiondburl = juliaupserver_base
.join(&format!(
"juliaup/versiondb/versiondb-{}-{}.json",
online_dbversion,
get_juliaup_target()
))
.with_context(|| "Failed to construct URL for version db download.")?;

let foo = tempfile::NamedTempFile::new_in(&paths.versiondb.parent().unwrap()).unwrap().into_temp_path()

Check failure on line 1432 in src/operations.rs

View workflow job for this annotation

GitHub Actions / Rustfmt Check

expected `;`, found `download_versiondb`

Check failure on line 1432 in src/operations.rs

View workflow job for this annotation

GitHub Actions / test-juliaup (x86_64-apple-darwin)

expected `;`, found `download_versiondb`

Check failure on line 1432 in src/operations.rs

View workflow job for this annotation

GitHub Actions / test-juliaup (x86_64-unknown-linux-gnu)

expected `;`, found `download_versiondb`

Check failure on line 1432 in src/operations.rs

View workflow job for this annotation

GitHub Actions / test-juliaup (x86_64-unknown-linux-musl)

expected `;`, found `download_versiondb`

Check failure on line 1432 in src/operations.rs

View workflow job for this annotation

GitHub Actions / check-juliaup (x86_64-apple-darwin)

expected `;`, found `download_versiondb`

Check failure on line 1432 in src/operations.rs

View workflow job for this annotation

GitHub Actions / check-juliaup (x86_64-unknown-linux-gnu)

expected `;`, found `download_versiondb`

download_versiondb(&onlineversiondburl.to_string(), &foo)
.with_context(|| {
format!(
"Failed to download new version db from {}.",
onlineversiondburl
)
})?;

temp_versiondb_download_path = Some(foo);
}
} else if local_dbversion.is_some() {
// If the bundled version is up-to-date we can delete any cached version db json file


delete_old_version_db = true;
}
}

let online_dbversion = download_juliaup_version(&dbversion_url.to_string())
.with_context(|| "Failed to download current version db version.")?;
let mut config_file = load_mut_config_db(paths).with_context(|| {
"`run_command_update_version_db` command failed to load configuration db."
})?;

let direct_download_etags = download_direct_download_etags(&mut config_file.data)?;
// This is our optimistic locking check: if someone changed the last modified
// field since we released the read-lock, we just give up
if config_file.data.last_version_db_update != old_last_version_db_update {
return Ok(());
}

for (channel, etag) in direct_download_etags {
let channel_data = config_file.data.installed_channels.get(&channel).unwrap();
Expand Down Expand Up @@ -1410,63 +1487,21 @@ pub fn update_version_db(paths: &GlobalPaths) -> Result<()> {

config_file.data.last_version_db_update = Some(chrono::Utc::now());

save_config_db(&mut config_file).with_context(|| "Failed to save configuration file.")?;

let bundled_dbversion = get_bundled_dbversion()
.with_context(|| "Failed to determine the bundled version db version.")?;

let local_dbversion = match std::fs::OpenOptions::new()
.read(true)
.open(&paths.versiondb)
{
Ok(file) => {
let reader = BufReader::new(&file);

if let Ok(versiondb) =
serde_json::from_reader::<BufReader<&std::fs::File>, JuliaupVersionDB>(reader)
{
if let Ok(version) = semver::Version::parse(&versiondb.version) {
Some(version)
} else {
None
}
} else {
None
}
}
Err(_) => None,
};

if online_dbversion > bundled_dbversion {
if local_dbversion.is_none() || online_dbversion > local_dbversion.unwrap() {
let onlineversiondburl = juliaupserver_base
.join(&format!(
"juliaup/versiondb/versiondb-{}-{}.json",
online_dbversion,
get_juliaup_target()
))
.with_context(|| "Failed to construct URL for version db download.")?;

download_versiondb(&onlineversiondburl.to_string(), &paths.versiondb).with_context(
|| {
format!(
"Failed to download new version db from {}.",
onlineversiondburl
)
},
)?;
}
} else if local_dbversion.is_some() {
// If the bundled version is up-to-date we can delete any cached version db json file
if let Some(foo) = temp_versiondb_download_path {
std::fs::rename(&foo, &paths.versiondb)?;
}
else if delete_old_version_db {
let _ = std::fs::remove_file(&paths.versiondb);
}

save_config_db(&mut config_file).with_context(|| "Failed to save configuration file.")?;

Ok(())
}

#[cfg(windows)]
fn download_direct_download_etags(
config_data: &mut JuliaupConfig,
config_data: &JuliaupConfig,
) -> Result<Vec<(String, String)>> {
use windows::core::HSTRING;
use windows::Web::Http::HttpMethod;
Expand Down

0 comments on commit 7c8ccd3

Please sign in to comment.