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

Adding support for "risky" writes with TransactionDB? #832

Open
siennathesane opened this issue Nov 15, 2023 · 0 comments
Open

Adding support for "risky" writes with TransactionDB? #832

siennathesane opened this issue Nov 15, 2023 · 0 comments

Comments

@siennathesane
Copy link

siennathesane commented Nov 15, 2023

I was reading through the rocksdb code base to figure out how to do range deletions with TransactionDB, and it look like there's a slightly "risky" way to do it with native writes within a transaction. I was digging through this library, and I'm not seeing support for TransactionDBWriteOptimizations or requisite support with the write() methods.

My use case is Raft log truncation with a TransactionDB. I have a range of Raft logs that I need to truncate, and since I can't use TransactionDB::DeleteRange(), I have to get creative. Theoretically I can just use a (prefix) iterator with min/max bounds, but that feels pretty inefficient. Personally, I'd rather just use a lock to ensure the transaction is isolated, then make the "risky" write with TransactionDBWriteOptimizations. Could support for this be added? How much effort would that be?

This is what I'm doing now, and it feels very inefficient:

    fn truncate(&mut self, meta_log_id: &MetaLogId) -> std::result::Result<(), StorageError> {
        let (log_id, cf_handle) = self.metalog_unwinder(&meta_log_id)?;

        // from where we're told to the end.
        let start = log_id.index.to_le_bytes();
        let from = u64::MAX.to_le_bytes();

        let read_opts = ReadOptions::default();
        read_opts.fill_cache(false);
        read_opts.set_iterate_range(start..from);
      
        let tx = self.db.transaction();
        let mut tx_iter = tx.iterator_cf_opt(&cf_handle, read_opts, IteratorMode::Start);

        for log in tx_iter {
            let (key, val) = log.unwrap();

            match tx.delete_cf(&cf_handle, key) {
                Ok(_) => {},
                Err(e) => {
                    let shard_id = meta_log_id.shard_id;
                    match tx.rollback() {
                        Ok(_) => return Err(MetaRaftLogStorageError(FailedTruncation(&shard_id, "tx rolled back successfully"))),
                        // todo (sienna): this feels hacky with `format!`, maybe fix later
                        Err(ve) => return Err(MetaRaftLogStorageError(FailedTruncation(&shard_id, format!("tx failed to rollback, this replica is likely fucked, {}", ve.to_string()).as_str())))
                    }
                }
            };
        }

        match tx.commit() {
            Ok(_) => Ok(()),
            Err(e) => MetaRaftLogStorageError(FailedTruncation(meta_log_id.shard_id, "failed to commit successful truncation tx"))
        }
    }

Theoretically I can do the work, but I'm new to rust and rocksdb, and I've not yet needed to directly use the FFI in rust. I do have some experience with cgo, so I can likely figure it out with some effort and questions.

Edit: added existing reference

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

No branches or pull requests

1 participant