forked from Deuxfleurs/garage
Don't do version & block_ref updates in background on deletion
This commit is contained in:
parent
5ae32972ef
commit
04acaea231
7 changed files with 56 additions and 46 deletions
5
TODO
5
TODO
|
@ -6,6 +6,11 @@ Finish the thing that sends blocks to other nodes if needed before deleting them
|
||||||
How are we going to test that our replication method works correctly?
|
How are we going to test that our replication method works correctly?
|
||||||
We will have to introduce lots of dummy data and then add/remove nodes many times.
|
We will have to introduce lots of dummy data and then add/remove nodes many times.
|
||||||
|
|
||||||
|
Repair:
|
||||||
|
- re-propagate object table version deletions to version table
|
||||||
|
- re-propagate version table deletion to block ref table
|
||||||
|
- re-propagate block ref table to rc
|
||||||
|
|
||||||
|
|
||||||
To do list
|
To do list
|
||||||
----------
|
----------
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use crate::background::*;
|
use crate::background::*;
|
||||||
use crate::data::*;
|
use crate::data::*;
|
||||||
|
use crate::error::Error;
|
||||||
use crate::table::*;
|
use crate::table::*;
|
||||||
|
|
||||||
use crate::block::*;
|
use crate::block::*;
|
||||||
|
@ -47,20 +48,17 @@ impl TableSchema for BlockRefTable {
|
||||||
type E = BlockRef;
|
type E = BlockRef;
|
||||||
type Filter = ();
|
type Filter = ();
|
||||||
|
|
||||||
async fn updated(&self, old: Option<Self::E>, new: Option<Self::E>) {
|
async fn updated(&self, old: Option<Self::E>, new: Option<Self::E>) -> Result<(), Error> {
|
||||||
let block = &old.as_ref().or(new.as_ref()).unwrap().block;
|
let block = &old.as_ref().or(new.as_ref()).unwrap().block;
|
||||||
let was_before = old.as_ref().map(|x| !x.deleted).unwrap_or(false);
|
let was_before = old.as_ref().map(|x| !x.deleted).unwrap_or(false);
|
||||||
let is_after = new.as_ref().map(|x| !x.deleted).unwrap_or(false);
|
let is_after = new.as_ref().map(|x| !x.deleted).unwrap_or(false);
|
||||||
if is_after && !was_before {
|
if is_after && !was_before {
|
||||||
if let Err(e) = self.block_manager.block_incref(block) {
|
self.block_manager.block_incref(block)?;
|
||||||
eprintln!("Failed to incref block {:?}: {}", block, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if was_before && !is_after {
|
if was_before && !is_after {
|
||||||
if let Err(e) = self.block_manager.block_decref(block) {
|
self.block_manager.block_decref(block)?;
|
||||||
eprintln!("Failed to decref block {:?}: {}", block, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matches_filter(entry: &Self::E, _filter: &Self::Filter) -> bool {
|
fn matches_filter(entry: &Self::E, _filter: &Self::Filter) -> bool {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
use crate::table::*;
|
use crate::table::*;
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||||
|
@ -71,7 +72,9 @@ impl TableSchema for BucketTable {
|
||||||
type E = Bucket;
|
type E = Bucket;
|
||||||
type Filter = ();
|
type Filter = ();
|
||||||
|
|
||||||
async fn updated(&self, _old: Option<Self::E>, _new: Option<Self::E>) {}
|
async fn updated(&self, _old: Option<Self::E>, _new: Option<Self::E>) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn matches_filter(entry: &Self::E, _filter: &Self::Filter) -> bool {
|
fn matches_filter(entry: &Self::E, _filter: &Self::Filter) -> bool {
|
||||||
!entry.deleted
|
!entry.deleted
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use crate::background::BackgroundRunner;
|
use crate::background::BackgroundRunner;
|
||||||
use crate::data::*;
|
use crate::data::*;
|
||||||
|
use crate::error::Error;
|
||||||
use crate::table::*;
|
use crate::table::*;
|
||||||
use crate::table_sharded::*;
|
use crate::table_sharded::*;
|
||||||
|
|
||||||
|
@ -101,30 +102,28 @@ impl TableSchema for ObjectTable {
|
||||||
type E = Object;
|
type E = Object;
|
||||||
type Filter = ();
|
type Filter = ();
|
||||||
|
|
||||||
async fn updated(&self, old: Option<Self::E>, new: Option<Self::E>) {
|
async fn updated(&self, old: Option<Self::E>, new: Option<Self::E>) -> Result<(), Error> {
|
||||||
let version_table = self.version_table.clone();
|
let version_table = self.version_table.clone();
|
||||||
if let (Some(old_v), Some(new_v)) = (old, new) {
|
if let (Some(old_v), Some(new_v)) = (old, new) {
|
||||||
// Propagate deletion of old versions
|
// Propagate deletion of old versions
|
||||||
self.background.spawn(async move {
|
for v in old_v.versions.iter() {
|
||||||
for v in old_v.versions.iter() {
|
if new_v
|
||||||
if new_v
|
.versions
|
||||||
.versions
|
.binary_search_by(|nv| nv.cmp_key().cmp(&v.cmp_key()))
|
||||||
.binary_search_by(|nv| nv.cmp_key().cmp(&v.cmp_key()))
|
.is_err()
|
||||||
.is_err()
|
{
|
||||||
{
|
let deleted_version = Version {
|
||||||
let deleted_version = Version {
|
uuid: v.uuid.clone(),
|
||||||
uuid: v.uuid.clone(),
|
deleted: true,
|
||||||
deleted: true,
|
blocks: vec![],
|
||||||
blocks: vec![],
|
bucket: old_v.bucket.clone(),
|
||||||
bucket: old_v.bucket.clone(),
|
key: old_v.key.clone(),
|
||||||
key: old_v.key.clone(),
|
};
|
||||||
};
|
version_table.insert(&deleted_version).await?;
|
||||||
version_table.insert(&deleted_version).await?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matches_filter(_entry: &Self::E, _filter: &Self::Filter) -> bool {
|
fn matches_filter(_entry: &Self::E, _filter: &Self::Filter) -> bool {
|
||||||
|
|
|
@ -105,7 +105,7 @@ pub trait TableSchema: Send + Sync {
|
||||||
type E: Entry<Self::P, Self::S>;
|
type E: Entry<Self::P, Self::S>;
|
||||||
type Filter: Clone + Serialize + for<'de> Deserialize<'de> + Send + Sync;
|
type Filter: Clone + Serialize + for<'de> Deserialize<'de> + Send + Sync;
|
||||||
|
|
||||||
async fn updated(&self, old: Option<Self::E>, new: Option<Self::E>);
|
async fn updated(&self, old: Option<Self::E>, new: Option<Self::E>) -> Result<(), Error>;
|
||||||
fn matches_filter(_entry: &Self::E, _filter: &Self::Filter) -> bool {
|
fn matches_filter(_entry: &Self::E, _filter: &Self::Filter) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -469,7 +469,7 @@ where
|
||||||
epidemic_propagate.push(new_entry.clone());
|
epidemic_propagate.push(new_entry.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.instance.updated(old_entry, Some(new_entry)).await;
|
self.instance.updated(old_entry, Some(new_entry)).await?;
|
||||||
self.system
|
self.system
|
||||||
.background
|
.background
|
||||||
.spawn(syncer.clone().invalidate(tree_key));
|
.spawn(syncer.clone().invalidate(tree_key));
|
||||||
|
@ -497,7 +497,7 @@ where
|
||||||
}
|
}
|
||||||
if let Some(old_val) = self.store.remove(&key)? {
|
if let Some(old_val) = self.store.remove(&key)? {
|
||||||
let old_entry = rmp_serde::decode::from_read_ref::<_, F::E>(&old_val)?;
|
let old_entry = rmp_serde::decode::from_read_ref::<_, F::E>(&old_val)?;
|
||||||
self.instance.updated(Some(old_entry), None).await;
|
self.instance.updated(Some(old_entry), None).await?;
|
||||||
self.system
|
self.system
|
||||||
.background
|
.background
|
||||||
.spawn(syncer.clone().invalidate(key.to_vec()));
|
.spawn(syncer.clone().invalidate(key.to_vec()));
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use crate::background::BackgroundRunner;
|
use crate::background::BackgroundRunner;
|
||||||
use crate::data::*;
|
use crate::data::*;
|
||||||
|
use crate::error::Error;
|
||||||
use crate::table::*;
|
use crate::table::*;
|
||||||
use crate::table_sharded::*;
|
use crate::table_sharded::*;
|
||||||
|
|
||||||
|
@ -67,26 +68,24 @@ impl TableSchema for VersionTable {
|
||||||
type E = Version;
|
type E = Version;
|
||||||
type Filter = ();
|
type Filter = ();
|
||||||
|
|
||||||
async fn updated(&self, old: Option<Self::E>, new: Option<Self::E>) {
|
async fn updated(&self, old: Option<Self::E>, new: Option<Self::E>) -> Result<(), Error> {
|
||||||
let block_ref_table = self.block_ref_table.clone();
|
let block_ref_table = self.block_ref_table.clone();
|
||||||
if let (Some(old_v), Some(new_v)) = (old, new) {
|
if let (Some(old_v), Some(new_v)) = (old, new) {
|
||||||
// Propagate deletion of version blocks
|
// Propagate deletion of version blocks
|
||||||
self.background.spawn(async move {
|
if new_v.deleted && !old_v.deleted {
|
||||||
if new_v.deleted && !old_v.deleted {
|
let deleted_block_refs = old_v
|
||||||
let deleted_block_refs = old_v
|
.blocks
|
||||||
.blocks
|
.iter()
|
||||||
.iter()
|
.map(|vb| BlockRef {
|
||||||
.map(|vb| BlockRef {
|
block: vb.hash.clone(),
|
||||||
block: vb.hash.clone(),
|
version: old_v.uuid.clone(),
|
||||||
version: old_v.uuid.clone(),
|
deleted: true,
|
||||||
deleted: true,
|
})
|
||||||
})
|
.collect::<Vec<_>>();
|
||||||
.collect::<Vec<_>>();
|
block_ref_table.insert_many(&deleted_block_refs[..]).await?;
|
||||||
block_ref_table.insert_many(&deleted_block_refs[..]).await?;
|
}
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matches_filter(entry: &Self::E, _filter: &Self::Filter) -> bool {
|
fn matches_filter(entry: &Self::E, _filter: &Self::Filter) -> bool {
|
||||||
|
|
6
test_delete.sh
Executable file
6
test_delete.sh
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
for FILE in $(find target/debug/deps); do
|
||||||
|
curl -v localhost:3900/$FILE -X DELETE -H 'Host: garage'
|
||||||
|
done
|
||||||
|
|
Loading…
Reference in a new issue