-
Notifications
You must be signed in to change notification settings - Fork 165
Conversation
this wasn't done originally even if PinMode was quite simple, just to see if there needs to be more complication later on.
perhaps the launder is more used term here.
5d7c382
to
edd0ac9
Compare
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.
Great job, transaction is better than Batch
, as it makes operations like read then write
atomic, the batch can only guarantee the a squence of write
things to be atomic 👍 .
.. I find a pontential risk that a transaction can block another transaction
#[async_trait] | ||
impl PinStore for KvDataStore { | ||
async fn is_pinned(&self, block: &Cid) -> Result<bool, Error> { | ||
is_pinned(self, block) | ||
async fn is_pinned(&self, cid: &Cid) -> Result<bool, 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.
Maybe this is the only place where transaction is not needed, as sled's single-key operations are atomic :)
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.
however, this looking like it can be just an atomic read makes me think it must be the wrong abstraction.. but if I recall correctly, this is called when removing a block through /api/v0/block/rm
so ... I guess it needs to stay for now.
match already_pinned { | ||
Some(PinMode::Recursive) => return Ok(false), | ||
Some(mode @ PinMode::Direct) | Some(mode @ PinMode::Indirect) => { | ||
// FIXME: this is probably another lapse in tests that both direct and |
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.
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.
Whoops, forgot to reply to this one. Yeah, this is a good point.
I've implemented (or at least tried to) the mem and fs so that the pins would not replace other pins, and I am surprised you found this in go-ipfs. I might also misremember, I did go back and forth here a lot. But I guess I'll need to confirm my thinking in upcoming PRs with more test cases, following the addition of spawn_blocking
.
|
||
_ if cid_str_with_prefix.starts_with("i") => { | ||
PinMode::Indirect | ||
// FIXME: this is still blocking ... might not be a way without a channel |
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 the pin listing is mainly used for GC, there will not be many concurrent call sto it1 , so may be a new thread can be spawned by something like
threadpool::spawn
to make the listing non-blocking. - I wonder why the PinStore and DataStore should be coupled, if it must be, can pin data be seperately putted in a alone sled tree. Then the filter of key that it should start with "pin." 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.
mainly used for GC, there will not be many concurrent call to it
Since it's available from the http api, I'd consider it beign callable at all times. For GC... I think the transactions might be helping out with it a lot. However, I am not too sure if we should attempt to build a similar GC than what go-ipfs has (not sure what is the present implementation) but I was thinking of trying to go through more of the route of maintaining a collectable set at all times through reference counting.
The reference counting would of probably require serializing all of the documents as graphs inside the blockstore, paving a way for a more monolithic store instead of trying to go with a number of different stores which are pluggable. I can't really see an design of such composed store which would be anywhere near consistent or durable.
so maybe a new thread can be spawned by something like
threadpool::spawn
tokio already has spawn_blocking
so I'd rather not entangle a new threadpool implementation here. I did not yet use the spawn_blocking
as that'd mess up the indentation at least and make this PR even less reviewable. I'll do this in a follow-up.
I wonder why the PinStore and DataStore should be coupled
There is no particular reason, this was more of a "convenient" quick decision I made to enable hacking the memory and fs based stores together, getting the "ball rolling" so to speak. However, I am still thinking that perhaps we should just put everything of Repo
behind a single monolithic sled/database implementation and not even try to view these as separate. Perhaps things like non-local storage of blocks (for example in a s3) should be viewed as extensions to a monolithic database backend rather than "interchangeable blocks". Abstracting over databases is really difficult either way.
pin data be separatedly put in a standalone sled tree. Then the filter of key that is should start with "pin." can be removed.
My thoughts exactly! Tried to keep a lid on the number of changes which already spiraled out of control so kept the current way :) I tried to write some more ideas in TODOs of the get_pin_key(cid: &Cid, mode: &Mode) -> String
fn.
|
||
// as we might loop over an over on the tx we might need this | ||
for id in ids.iter() { | ||
// FIXME: this is blocking ... |
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 same, considering no may calls at the same time, a new thread may be used to spawn the call.
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.
RE: blocking in general. Doing a lot of blocking stuff will be detrimental to any runtimes (such as tokio) performance as futures simply should not block, and we cannot rely on the database being cached at any times (re: sled docs on the topic). So will be following-up with a spawn_blocking
linked in the above comment.
My understanding is that |
There is a example(sorry, this cannot be runned on the playground as there is a sled external crate), when a transaction is not over, anther transaction will be blocked. So like |
Thanks for the example. Yeah, using sleeps or external waits inside would of course make it worse, similarly to blocking inside an async runtime. I had been expecting that
Yeah I am guessing it's just one of those things which doesn't need solving. However it'd be a good idea to have some sort of mechanism to catch the slowing down, though running these in a RE: the PR I assume this would be 👍 from you? |
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.
Good jobs! 👍 Yes, 2021 will be the year of observability hope
great, thanks! bors r+ |
Build succeeded: |
This enables the pinstore to use transactions for almost all operations. Big ticket TODOs remaining:
In smaller changes:
PinModeRequirement
out of theOption<PinMode>
which is hopefully more readable (list, query)CC: @fetchadd, got the PR I promised made. Do you have any ideas? Sadly the changeset is probably quite difficult to review. I could split it up to have just the small fixes followed by the transactional stuff, but the smaller changes can probably by diffing only up until some commits.