spacejam / sled

the champagne of beta embedded databases
Apache License 2.0
8.09k stars 383 forks source link

Tree can not be shared across threads safely #1507

Open spacekookie opened 3 months ago

spacekookie commented 3 months ago

I've been migrating a project to sled from a different embedded db and I'm getting some strange compilation errors:

error: future cannot be sent between threads safely
   --> ratman/src/journal/page.rs:99:85
    |
99  |       async fn fetch(&self, reference: &BlockReference) -> IoResult<Option<Block<L>>> {
    |  _____________________________________________________________________________________^
100 | |         match self.get(&reference.to_string()) {
101 | |             Err(RatmanError::Io(io)) => Err(io),
102 | |             Ok(Some(BlockData { data, valid })) if valid => Ok(Some(data.to_block())),
103 | |             _ => Ok(None),
104 | |         }
105 | |     }
    | |_____^ future created by async block is not `Send`
    |
    = help: within `Tree`, the trait `Sync` is not implemented for `RefCell<ebr::Inner<concurrent_map::Deferred<sled::ObjectId, sled::Object<1024>, 64>, 128, 128>>`, which is required by `{async block@ratman/src/journal/page.rs:99:85: 105:6}: std::marker::Send`
    = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`

The type this is implemented for is:

pub struct CachePage<T: Serialize + DeserializeOwned>(pub Arc<sled::Tree>, pub PhantomData<T>);

And this is the trait definition that causes problems:

#[async_trait]
impl<const L: usize> BlockStorage<L> for CachePage<BlockData> {
    async fn store(&self, block: &Block<L>) -> IoResult<()> {
        self.insert(
            block.reference().to_string(),
            &BlockData {
                // We can unwrap here because the blocks were previously
                // verified to be valid
                data: StorageBlock::reconstruct(block.as_slice()).unwrap(),
                valid: true,
            },
        )?;
        Ok(())
    }

    async fn fetch(&self, reference: &BlockReference) -> IoResult<Option<Block<L>>> {
        match self.get(&reference.to_string()) {
            Err(RatmanError::Io(io)) => Err(io),
            Ok(Some(BlockData { data, valid })) if valid => Ok(Some(data.to_block())),
            _ => Ok(None),
        }
    }
}

Is this a bug or am I holding the Tree wrong? Oh and I'm using version 1.0.0-alpha.121

rail-ka commented 3 months ago

I am not a Sled developer, but I have encountered a similar problem. The solution was not to share the Tree between threads, but to clone it for each thread.

P.S. It is also better to wrap Tree in Box, because the size on the stack is 7648 bytes, and there may be a stack overflow error.