-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add sst file snapshot impl #1618
Conversation
Cargo.toml
Outdated
@@ -62,6 +62,7 @@ signal = "0.2" | |||
|
|||
[dependencies.rocksdb] | |||
git = "https://github.com/pingcap/rust-rocksdb.git" | |||
branch = "hhkbp2/sst-file-snapshot" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can use master now
src/raftstore/store/snap.rs
Outdated
let mut buf = vec![0; DIGEST_BUFFER_SIZE]; | ||
loop { | ||
let n = try!(f.read(&mut buf[..])); | ||
if n == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
digest.write(&but[..n])
if n < buf.len() {
return sum
}
src/raftstore/store/snap.rs
Outdated
} | ||
} | ||
|
||
fn parse_cf_size_checksums(kvs: &[KeyValue]) -> RaftStoreResult<Vec<(String, u64, u32)>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer define a struct to contain the meta and use json to do the codec directly, no need to encode/decode manually.
Btw, we can also define a protobuf type too. But json is simple here.
src/raftstore/store/snap.rs
Outdated
try!(fs::create_dir_all(dir_path.as_path())); | ||
} | ||
let prefix = format!("{}_{}", SNAP_GEN_PREFIX, key); | ||
let display_path = Snap::get_display_path(&dir_path, &prefix); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using a unique folder to save all CF SST files and meta may be better? like /dir/gen_{key}/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so we can rename, delete the folder directly later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's commonly seen that a single directory is used for raw data files, e.g. RocksDB use a single db dir to store sst files of all LSM levels.
src/raftstore/store/snap.rs
Outdated
if !need_to_pack(cf) { | ||
continue; | ||
} | ||
try!(self.next_file(cf.to_owned())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seem that it is not for next file, but to get the correct writer index with the CF.
if buf.len() == 0 { | ||
return Ok(0); | ||
} | ||
while self.cf_index < self.cf_files.len() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not using enumerate directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It needs to record the current index to self.cf_index
.
src/raftstore/store/snap.rs
Outdated
let mut f = self.meta_file.file.as_mut().unwrap(); | ||
try!(f.write_all(&v[..])); | ||
try!(f.flush()); | ||
try!(fs::rename(&self.meta_file.tmp_path, &self.meta_file.path)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the file is still opened, can it be renamed?
meta.get_size(), | ||
meta.get_checksum())); | ||
} | ||
cf_file.size = meta.get_size(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it a bug that meta exists but the cf file doesn't?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean exactly "is it a bug"?
The data corruption could cause this exception: the cf files are deleted/gone, but the meta file remains.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then it should return an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The checking is added in load_snapshot_meta()
.
src/raftstore/store/snap.rs
Outdated
} | ||
let meta = found.unwrap(); | ||
if file_exists(&cf_file.path) { | ||
try!(check_file_size_and_checksum(&cf_file.path, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's about to generate the snapshot, when either file size or checksum doesn't match, the file should be deleted; otherwise should panic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
address comment. The panic should be raised properly by upper level code.
} | ||
let file = try!(OpenOptions::new() | ||
.write(true) | ||
.create_new(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may fail if meta file exists.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The meta file is written at last. If the meta file exists, this snapshot exists(normally, if there is no data corruption). It checks exists()
at the begin of this function. So if the meta file exists, the control flow will not get to this create_new()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if the meta file exists but some cf files are missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then it must be through some kind of data corruption, then it return an error here.
src/raftstore/store/snap.rs
Outdated
// set snapshot meta data | ||
let total_size = try!(self.total_size()); | ||
snap_data.set_file_size(total_size); | ||
snap_data.set_version(SNAPSHOT_VERSION); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems v2 or v1 share the same SNAPSHOT_VERSION.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My mistake, they don't.
stat: &mut SnapshotStatistics) | ||
-> RaftStoreResult<()> { | ||
if self.exists() { | ||
match self.validate() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file is checked twice when it exists already. First time is in init_for_building, second time is here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the point here? Are you suggesting merging two piece of code together?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I mean is the file only needs to be checked once. Either checks can be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are two pieces of code which are called on initialization and building. And the exists()
checks the existence for different purpose. It's doable to add some flag var like exists
, but it's simpler to just call a simple function exists()
.
|
||
pub struct Snap { | ||
display_path: String, | ||
cf_files: Vec<CfFile>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems using HashMap
is more readable than a Vec and an index.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's good to use a simple vector and index combination. The vector specifies the cf sequence, the index could be set to upper bound to indicate that there is no more capacity to read/to write. And it's simple and fast.
return Err(io::Error::new(ErrorKind::Other, e)); | ||
} | ||
} | ||
let size = try!(get_file_size(&cf_file.tmp_path)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible that the size doesn't match the actual file when the file handle is dropped without flush?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function only runs when a snapshot is built, so the cf file size is first initialized as snapshot meta. And the file is always being flushed when closed.
I don't get it by "the size doesn't match the actual file"? Could you elaborate on this?
src/raftstore/store/snap.rs
Outdated
cf_file.checksum = try!(calc_crc32(&cf_file.path)); | ||
} else { | ||
// Clean up the `tmp_path` if this cf file is empty. | ||
delete_file_if_exist(&cf_file.tmp_path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The clean up job should be implemented in Drop
trait in case there is any missing condition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't know what you mean by the missing condition. Drop::drop
takes care of the situation where the snapshot file is written partly on error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought Drop
was not implemented when I saw the temp file was deleted explicitly here.
fs::metadata(&self.meta_file.path) | ||
} | ||
|
||
fn total_size(&self) -> io::Result<u64> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not include the size of meta file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
total_size
returns the total size appended after the snapshot meta.
src/raftstore/store/snap.rs
Outdated
snap_data.set_meta(self.meta_file.meta.clone()); | ||
|
||
// add size | ||
let mut size_track = self.size_track.wl(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The size should be updated once the snapshot file is saved, otherwise if there are several failure, there will be a big gap between the size_track and the actual snap directory size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's updated after the snapshot file is saved, actually. It will not get to any error after the meta file is done saving.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I mean is it should be updated at https://github.com/pingcap/tikv/pull/1618/files#diff-d73c1547780d463f5ca96c85a279da7cR1074.
src/raftstore/store/snap.rs
Outdated
use util::transport::SendCh; | ||
use util::{HandyRwLock, HashMap}; | ||
|
||
use super::engine::Snapshot as DbSnapshot; | ||
use super::peer_storage::JOB_STATUS_CANCELLING; | ||
|
||
// Data in CF_RAFT should be excluded for a snapshot. | ||
const SNAPSHOT_CFS: &'static [CfName] = &[CF_DEFAULT, CF_LOCK, CF_WRITE]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be used in all the place related to snapshot too, like region.rs
.
If there are snapshot files that have no correspond meta file, they won't be deleted by gc. |
src/raftstore/store/snap.rs
Outdated
snapshot_meta: SnapshotMeta, | ||
size_track: Arc<RwLock<u64>>) | ||
-> RaftStoreResult<Snap> { | ||
let cfs = try!(check_snapshot_meta(&snapshot_meta)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check is redundant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why it's redundant? This check_snapshot_meta
plays its role to check the validity of the snapshot meta.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set_snapshot_meta
should do the job properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set_snapshot_meta
checks whether the size and checksum is correct when snapshot file exists. check_snapshot_meta
checks whether the received snapshot meta has valid cf name and number. They are different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need two function to do the check. When setting snapshot meta, the size, checksum and cfs all should be as expected.
It will be drop during gc procedure. |
No, for sending, it will report error at https://github.com/pingcap/tikv/blob/hhkbp2/sst-file-snapshot-v2/src/raftstore/store/snap.rs#L887; for receiving, it will be skipped at https://github.com/pingcap/tikv/blob/hhkbp2/sst-file-snapshot-v2/src/raftstore/store/snap.rs#L1823. |
PTAL |
src/raftstore/store/snap.rs
Outdated
} | ||
} | ||
|
||
fn get_snapshot_meta(cf_files: &[CfFile]) -> RaftStoreResult<SnapshotMeta> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/get_snapshot_meta/gen_snapshot_meta
LGTM |
PTAL @BusyJay |
// load snapshot meta if meta_file exists | ||
if file_exists(&s.meta_file.path) { | ||
if let Err(e) = s.load_snapshot_meta() { | ||
if !to_build { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can load the meta lazily instead of passing a to_build explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The meta is needed in the most common interface exists()
. Snap
is usually used as a short-time living object in the context. It's not shared or reused. It would not benefit to delay the meta loading, but just make the code complicated.
src/util/file.rs
Outdated
} | ||
|
||
pub fn delete_file_if_exist(file: &PathBuf) { | ||
if !file_exists(file) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file can be removed directly and then omit the not found error. So there will be only once sys call.
src/raftstore/store/snap.rs
Outdated
try!(self.meta_file.meta.write_to_vec(&mut v)); | ||
try!(self.meta_file.file.take().unwrap().write_all(&v[..])); | ||
try!(fs::rename(&self.meta_file.tmp_path, &self.meta_file.path)); | ||
let mut size_track = self.size_track.wl(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be updated at L1235.
Ok(snapshot_meta) | ||
} | ||
|
||
fn set_snapshot_meta(&mut self, snapshot_meta: SnapshotMeta) -> RaftStoreResult<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should be checked if the cfs in snapshot_meta is the same as SNAPSHOT_CFS
.
Ping @BusyJay |
src/raftstore/store/snap.rs
Outdated
let found = snapshot_meta.get_cf_files().iter().find(|x| x.get_cf() == cf_file.cf); | ||
if found.is_none() { | ||
return Err(box_err!("cf {} doesn't exist in snapshot meta", cf_file.cf)); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems L977~L981 can be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad. Forget to clean up the old code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rest LGTM
LGTM |
Hi,
This PR tries to add impl for sst file snapshot. It's split from PR #1561