Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Add file-based blob store #2554

Closed
wants to merge 18 commits into from
Closed

Conversation

MarkJr94
Copy link
Contributor

@MarkJr94 MarkJr94 commented Jan 25, 2019

This is a work in progress replacement for the storage functionality of DbLedger.

See Issue #2566

upcoming subtasks:

  • partition slot folder paths such that slot 0x11_22_33_44_55_66_77_88 maps to folder root/0x11/22/33/44/55/66/77/88
  • store more metadata (stop using solana::db_ledger::SlotMeta)
  • store and retrieve erasure codes
  • write many more tests
  • utilize parallelism in writing - maybe unnecessary?
  • utilize parallelism in reading - maybe unnecessary?
  • write more accessor functions
    • per-slot consecutive entry retrieval
    • per-slot consecutive blob/data retrieval
    • consecutive entry retrieval across slots
    • consecutive blob retrieval across slots

@MarkJr94 MarkJr94 added the work in progress This isn't quite right yet label Jan 25, 2019
@MarkJr94 MarkJr94 requested a review from rob-solana January 25, 2019 12:49
@garious
Copy link
Contributor

garious commented Jan 28, 2019

Can you create an issue (maybe several) to track this work and add it to your PR description?

@MarkJr94
Copy link
Contributor Author

@garious on it

@MarkJr94 MarkJr94 force-pushed the blob-store branch 2 times, most recently from 3bc54e4 to 8dd82a5 Compare January 30, 2019 03:11
let mut buf = [0u8; INDEX_RECORD_SIZE as usize];
while let Ok(_) = index_file.read_exact(&mut buf) {
let index = BigEndian::read_u64(&buf[0..8]);
if index == blob_index {
Copy link
Contributor

Choose a reason for hiding this comment

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

this code takes N/2 to find the index record, it could be constant time

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wouldn't I have to ensure that indexes were written in-order to make that possible?

Copy link
Contributor

Choose a reason for hiding this comment

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

no, you can seek() to the spot you need to write() at in the index file. the file will grow automatically, and will be filled with zeros (which you can call "invalid" offsets)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it. I'm already doing this in another place, will make this change tomorrow

// Generate a num_reads sized random sample of indexes in range [0, total_blobs - 1],
// simulating random reads
let mut rng = rand::thread_rng();
let indexes: Vec<usize> = (0..num_reads)
Copy link
Contributor

Choose a reason for hiding this comment

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

are you intending to verify that caching helps, once implemented?

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've also experimented with parallelizing writing (partitioned based on slots) but I couldn't get it to work safely without needing to copy so much memory that it was much slower overall.

The little caching I've done so far made benchmark ns/iter ~70-80% of db_ledger benchmarks when before it was about ~110-120% .

I tried several different ways of exploiting concurrency including using tokio and futures, creating a separate writer thread that communicated over std::sync::mpsc and crossbeam-channel:: channels, etc.

But I could not do it both 1) safely 2) without so much copying that it made everything much slower.

@aeyakovenko
Copy link
Member

can you guys sync up with @sakridge on sakridge@aea2639

@garious garious changed the title File based blob store Add file-based blob store Feb 4, 2019
pub struct RecordFile<T> {
file: File,
current_offset: u64,
buf: RefCell<Vec<u8>>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is buf in the RecordFile struct and not just a stack variable in the calling functions if needed?

Copy link
Contributor Author

@MarkJr94 MarkJr94 Feb 5, 2019

Choose a reason for hiding this comment

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

That was my original goal, but I ran into the fact that there's no way to use an associated consts from a trait in an array expression currently [1] so I decided to use a single vector that was sized appropriately.

[1] rust-lang/rust#42863

@pgarg66
Copy link
Contributor

pgarg66 commented Feb 5, 2019

@MarkJr94 , I am trying to build this PR locally, and getting these build errors. Does it need rebase?

  Compiling solana v0.12.0 (/home/pankaj/pgarg66/solana)
error[E0658]: imports can only refer to extern crate names passed with `--extern` on stable channel (see issue #53130)
  --> src/blob_store.rs:21:5
   |
21 | use store::Key;
   |     ^^^^^
...
25 | pub mod store;
   | -------------- not an extern crate passed with `--extern`
   |
note: this import refers to the module defined here
  --> src/blob_store.rs:25:1
   |
25 | pub mod store;
   | ^^^^^^^^^^^^^^

error[E0658]: imports can only refer to extern crate names passed with `--extern` on stable channel (see issue #53130)
   --> src/blob_store.rs:135:13
    |
25  | pub mod store;
    | -------------- not an extern crate passed with `--extern`
...
135 |         use store::Named;
    |             ^^^^^
    |
note: this import refers to the module defined here
   --> src/blob_store.rs:25:1
    |
25  | pub mod store;
    | ^^^^^^^^^^^^^^

error[E0658]: imports can only refer to extern crate names passed with `--extern` on stable channel (see issue #53130)
   --> src/blob_store.rs:220:13
    |
25  | pub mod store;
    | -------------- not an extern crate passed with `--extern`
...
220 |         use store::Named;
    |             ^^^^^
    |
note: this import refers to the module defined here
   --> src/blob_store.rs:25:1
    |
25  | pub mod store;
    | ^^^^^^^^^^^^^^

error[E0658]: use of unstable library feature 'int_to_from_bytes' (see issue #52963)
  --> src/blob_store/store_impl.rs:15:22
   |
15 |     let splat = slot.to_be_bytes();
   |                      ^^^^^^^^^^^

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0658`.
error: Could not compile `solana`.

@MarkJr94
Copy link
Contributor Author

MarkJr94 commented Feb 5, 2019

@pgarg66 I'm looking into it right now, issue is likely because I have been using the nightly compiler locally.

@MarkJr94
Copy link
Contributor Author

MarkJr94 commented Feb 5, 2019

@pgarg66 should now build

Copy link
Contributor

@pgarg66 pgarg66 left a comment

Choose a reason for hiding this comment

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

@MarkJr94 , I think I am making assumptions while reviewing the code. Do you happen to have a small writeup/diagram that explains how different pieces interact with each other? My current understanding is this:

  1. BlobStore is the top level interface, and it exports functionality to store blobs per slot. Also it tracks the meta information for the slot (received, consumed, num blobs etc)
  2. Store provides get/put APIs for blobs and meta
  3. StoreImpl is providing caching and storing functionality
  4. RecordFile is filesystem wrapper

Also, there's a ton of use of Generics. Wondering if it's really needed?

I think some sort of documentation will help me understand the code better.

}

#[inline]
pub fn put_dyn<T>(&mut self, column: &str, key: Key, obj: T) -> Result<()>
Copy link
Contributor

Choose a reason for hiding this comment

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

These methods (gets/puts) are mostly called with the same type of T. Any good reason to use generics here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The intention was just to make things generic so that I wasn't duplicating code. The traits are definitely excessive I think. They'll be replaced with methods that just accept any T: Serialize for put and returns bytes or any T: Deserialize for gets.

@sakridge
Copy link
Contributor

sakridge commented Feb 7, 2019

@MarkJr94 Can we also try to split this PR into something smaller? Looks like RecordFile can be a separate PR, then look at the layers on top of that that can be merged.

@MarkJr94
Copy link
Contributor Author

@pgarg66 Yes I should've started with a design document. I'll have one up tomorrow, linked in the issue, (possibly as a PR to the book).

@sakridge Yes I think I'll split this up. I'm not sure if should close this PR for now then? The issue #2554 is where I'm tracking what I"m doing now.

@MarkJr94 MarkJr94 added the noCI Suppress CI on this Pull Request label Feb 14, 2019
@MarkJr94 MarkJr94 closed this Feb 14, 2019
yihau pushed a commit to yihau/solana that referenced this pull request Aug 13, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
noCI Suppress CI on this Pull Request work in progress This isn't quite right yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants