Skip to content

Commit

Permalink
Harden reflog updates from transient errors
Browse files Browse the repository at this point in the history
  • Loading branch information
w4 committed Dec 31, 2023
1 parent 7d804b9 commit e4e00b1
Showing 1 changed file with 74 additions and 50 deletions.
124 changes: 74 additions & 50 deletions src/database/indexer.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::{
borrow::Cow,
collections::HashSet,
ffi::OsStr,
fmt::Debug,
path::{Path, PathBuf},
};

use git2::{ErrorCode, Sort};
use git2::{ErrorCode, Reference, Sort};
use ini::Ini;
use time::OffsetDateTime;
use tracing::{error, info, info_span, instrument, warn};
Expand All @@ -27,11 +29,14 @@ pub fn run(scan_path: &Path, db: &sled::Db) {

info!("Flushing to disk");

db.flush().unwrap();
if let Err(error) = db.flush() {
error!(%error, "Failed to flush database to disk");
}

info!("Finished index update");
}

#[instrument(skip(db))]
fn update_repository_metadata(scan_path: &Path, db: &sled::Db) {
let mut discovered = Vec::new();
discover_repositories(scan_path, &mut discovered);
Expand All @@ -42,12 +47,21 @@ fn update_repository_metadata(scan_path: &Path, db: &sled::Db) {
let id = Repository::open(db, relative)
.unwrap()
.map_or_else(|| RepositoryId::new(db), |v| v.get().id);
let name = relative.file_name().unwrap().to_string_lossy();
let Some(name) = relative.file_name().map(OsStr::to_string_lossy) else {
continue;
};
let description = std::fs::read(repository.join("description")).unwrap_or_default();
let description = Some(String::from_utf8_lossy(&description)).filter(|v| !v.is_empty());

let repository_path = scan_path.join(relative);
let git_repository = git2::Repository::open(repository_path.clone()).unwrap();

let git_repository = match git2::Repository::open(repository_path.clone()) {
Ok(v) => v,
Err(error) => {
warn!(%error, "Failed to open repository {} to update metadata, skipping", relative.display());
continue;
}
};

Repository {
id,
Expand Down Expand Up @@ -107,54 +121,64 @@ fn update_repository_reflog(scan_path: &Path, db: &sled::Db) {
continue;
}

let span = info_span!(
"branch_index_update",
reference = reference_name.as_ref(),
repository = relative_path
);
let _entered = span.enter();

info!("Refreshing indexes");

let commit_tree = db_repository
.get()
.commit_tree(db, &reference_name)
.unwrap();

if let (Some(latest_indexed), Ok(latest_commit)) =
(commit_tree.fetch_latest_one(), reference.peel_to_commit())
{
if latest_commit.id().as_bytes() == &*latest_indexed.get().hash {
info!("No commits since last index");
continue;
}
if let Err(error) = branch_index_update(
&reference,
&reference_name,
&relative_path,
db_repository.get(),
db,
&git_repository,
) {
error!(%error, "Failed to update reflog for {relative_path}@{reference_name}");
}
}
}
}

// TODO: only scan revs from the last time we looked
let mut revwalk = git_repository.revwalk().unwrap();
revwalk.set_sorting(Sort::REVERSE).unwrap();
if let Err(error) = revwalk.push_ref(&reference_name) {
error!(%error, "Failed to revwalk reference");
continue;
}
#[instrument(skip(reference, db_repository, db, git_repository))]
fn branch_index_update(
reference: &Reference<'_>,
reference_name: &str,
relative_path: &str,
db_repository: &Repository<'_>,
db: &sled::Db,
git_repository: &git2::Repository,
) -> Result<(), anyhow::Error> {
info!("Refreshing indexes");

let commit_tree = db_repository.commit_tree(db, reference_name)?;

if let (Some(latest_indexed), Ok(latest_commit)) =
(commit_tree.fetch_latest_one(), reference.peel_to_commit())
{
if latest_commit.id().as_bytes() == &*latest_indexed.get().hash {
info!("No commits since last index");
return Ok(());
}
}

let mut i = 0;
for rev in revwalk {
let commit = git_repository.find_commit(rev.unwrap()).unwrap();
let author = commit.author();
let committer = commit.committer();
// TODO: only scan revs from the last time we looked
let mut revwalk = git_repository.revwalk()?;
revwalk.set_sorting(Sort::REVERSE)?;
revwalk.push_ref(reference_name)?;

Commit::new(&commit, &author, &committer).insert(&commit_tree, i);
i += 1;
}
let mut i = 0;
for rev in revwalk {
let commit = git_repository.find_commit(rev?)?;
let author = commit.author();
let committer = commit.committer();

// a complete and utter hack to remove potentially dropped commits from our tree,
// we'll need to add `clear()` to sled's tx api to remove this
for to_remove in (i + 1)..(i + 100) {
commit_tree.remove(to_remove.to_be_bytes()).unwrap();
}
}
Commit::new(&commit, &author, &committer).insert(&commit_tree, i);
i += 1;
}

// a complete and utter hack to remove potentially dropped commits from our tree,
// we'll need to add `clear()` to sled's tx api to remove this
for to_remove in (i + 1)..(i + 100) {
commit_tree.remove(to_remove.to_be_bytes())?;
}

Ok(())
}

#[instrument(skip(db))]
Expand Down Expand Up @@ -212,13 +236,13 @@ fn update_repository_tags(scan_path: &Path, db: &sled::Db) {
}

#[instrument(skip(scan_path, db_repository, db))]
fn open_repo(
fn open_repo<P: AsRef<Path> + Debug>(
scan_path: &Path,
relative_path: &str,
relative_path: P,
db_repository: &Repository<'_>,
db: &sled::Db,
) -> Option<git2::Repository> {
match git2::Repository::open(scan_path.join(relative_path)) {
match git2::Repository::open(scan_path.join(relative_path.as_ref())) {
Ok(v) => Some(v),
Err(e) if e.code() == ErrorCode::NotFound => {
warn!("Repository gone from disk, removing from db");
Expand All @@ -230,7 +254,7 @@ fn open_repo(
None
}
Err(error) => {
warn!(%error, "Failed to reindex {relative_path}, skipping");
warn!(%error, "Failed to reindex, skipping");
None
}
}
Expand Down

0 comments on commit e4e00b1

Please sign in to comment.