Skip to content
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

engine: persist data to kernel #143

Merged
merged 24 commits into from
Dec 7, 2021
Merged

engine: persist data to kernel #143

merged 24 commits into from
Dec 7, 2021

Conversation

huachaohuang
Copy link
Contributor

@huachaohuang huachaohuang commented Dec 1, 2021

This PR enables the hash engine to persist data in a kernel. It demonstrates the interaction between Engine and Kernel.

Note that our kernel is still buggy as described here, so the engine recovery thing doesn't work if we test with more records here. I will fix that on the kernel side later.

Closes #59

@huachaohuang huachaohuang added this to the Version 0.2 milestone Dec 1, 2021
src/kernel/src/mem/mod.rs Outdated Show resolved Hide resolved
@zojw
Copy link
Contributor

zojw commented Dec 1, 2021

it seems we have multiple usage modes, the two most obvious ways are "as-storage-lib" or "as-storage-service".....

it let us think a question:

  • do we also need to handle logic like rocksdb's "OPTION-XXXX" file?
  • does components assembly logic is also like "using mem or file" also need persist as config file?
  • maybe info like OPTION also need in "as-storage-service mode" and where are they stored?

@huachaohuang
Copy link
Contributor Author

it seems we have multiple usage modes, the two most obvious ways are "as-storage-lib" or "as-storage-service".....

it let us think a question:

  • do we also need to handle logic like rocksdb's "OPTION-XXXX" file?
  • does components assembly logic is also like "using mem or file" also need persist as config file?
  • maybe info like OPTION also need in "as-storage-service mode" and where are they stored?

Yes, we need to persist the kernel options once a kernel is created. It is very dangerous to use a kernel in one way and then switch to another way. So we should provide some validation when a kernel is open.

pub async fn set(&self, ts: Timestamp, key: Vec<u8>, value: Vec<u8>) -> Result<()> {
let current = self.current_version().await;
current.set(ts, key, value).await?;
if let Some((imm, version)) = current.should_flush().await {
Copy link
Contributor

@zojw zojw Dec 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe here has some concurrency problem? two thread set at the same time maybe generate two immutable table & version... 🧐

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need to prevent flushing multiple memory tables at the same time, though. There are still a lot of work to do before this engine can work decently 🤣

@huachaohuang huachaohuang mentioned this pull request Dec 5, 2021
6 tasks
@huachaohuang
Copy link
Contributor Author

@zojw @tisonkun This PR is ready for review :)

@tisonkun
Copy link
Contributor

tisonkun commented Dec 6, 2021

@huachaohuang thanks for your reminder! I'm reviewing this PR now.

Copy link
Contributor

@tisonkun tisonkun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for preparing this PR! Comments inline.

I'll take a closer look at engine.rs later.

src/engine/hash/src/engine.rs Outdated Show resolved Hide resolved
src/engine/hash/src/format.rs Outdated Show resolved Hide resolved
src/engine/hash/src/format.rs Outdated Show resolved Hide resolved
src/engine/hash/src/memtable.rs Show resolved Hide resolved
for object in &update.add_objects {
// We assume that objects are flushed from the oldest immtable to the newest.
let reader = version.bucket.new_sequential_reader(object).await?;
let table_reader = TableReader::new(reader).await?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mark a improvement~ maybe we can build all readers first then consume readers :)

logic like

for {
	// build readers
}
for {
	// build table reader
}

then for gRPC impl, it can pipeline send requests to remote

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds a good idea.

for event in events {
ts += 1;
let (key, value) = codec::decode_record(&event.data)?;
current.put(ts, key.to_owned(), value.to_owned()).await;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to check MEMTABLE_SIZE here after recovery~?

it seems some rocksdb-like engines do that(it also has smaller memtable size threshold than normal flush operation, restart process maybe produce more sst file - -), I'm not sure is any practice need do it 😕

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think so. But maybe leave it as a future improvement. This PR has done enough.

@@ -40,8 +37,8 @@ pub struct Engine<K: Kernel> {
stream: K::Stream,
bucket: K::Bucket,
current: Arc<Mutex<Arc<EngineVersion<K>>>>,
last_ts: Arc<Mutex<Timestamp>>,
last_number: Arc<AtomicU64>,
last_timestamp: Arc<Mutex<Timestamp>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you rename the field to last_timestamp?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I can't give a strong reason here. Just thought that if I have multiple last_xxx fields here, maybe I'd better make them more verbose.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tisonkun Any more comments? If not, I am going to merge it then.

Copy link
Contributor

@zojw zojw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@tisonkun tisonkun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@huachaohuang
Copy link
Contributor Author

Thanks for your review. I believe this is a big step to complement the bridge between Engine and Kernel :)

@huachaohuang huachaohuang merged commit c19e631 into engula:main Dec 7, 2021
@huachaohuang huachaohuang deleted the engine branch December 7, 2021 04:48

let mut update = KernelUpdate::default();
let last_ts = encode_u64_meta(imm.last_update_timestamp().await);
update.set_meta(LAST_TIMESTAMP, last_ts);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after some learning, it seems we'd better add check last_ts > [current_last_ts] before do update.

someone ingest SST maybe contains higher ts value than imm.last_update_timestamp

but it's ok for now, because we don't support ingest 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the engine is quite buggy for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Settle down the project layout
3 participants