-
-
Notifications
You must be signed in to change notification settings - Fork 0
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
refactor: Adjustments to Store
s & Agent
s
#9
Conversation
778fd84
to
32164a3
Compare
…t-lifetime refactor: Make `Agent` take `DID` by value
I missed a step on this fn get(
&self,
cid: Cid,
) -> Result<Option<Arc<Invocation<T, DID, V, C>>>, Self::InvocationStoreError>;
UPDTAE: See comment lower in the thread |
Also while we're talking about it: there's two approaches to the type signature. The nested version is what I have here... // Current
get(/* ... */) -> Result<Option<Invocation<T, DID, V, C>>, Self::InvocationStoreError>;
// ^^^^^^
// `Ok` branch include non-happy path ...where struct StoreErr<T> {
NotFound(Cid),
DbErr(T)
}
get(/* ... */) -> Result<Invocation<T, DID, V, C>>, StoreErr<Self::InvocationStoreError>>;
// ^^^^^^^^
// HERE This cleans up the type a bit by putting anything not on the happy path over in the |
Maybe to expose my mental model: The I'm almost certainly missing a part of the picture, so mostly using this as a learning opportunity :) |
(EDIT: I reread the question a little later now, and I think I misunderstood it slightly. The below is still helpful, I think, but: Yes, immutable references are thread-safe. But it's not about thread-safety here, it's about not making the Requiring to return a With both The reason I went for They don't necessarily need to be
I think the |
Ah, right, this is the bit that I keep missing. I keep thinking of it as almost like an actor where there's some long-lived location representing "the database" that can hold various bits of memory, but that's not necessarily the case — I mean, you could build this, but that's way more work than an |
pub struct MemoryStore< | ||
DID: did::Did + Ord = did::preset::Verifier, | ||
V: varsig::Header<C> = varsig::header::Preset, | ||
C: Codec + TryFrom<u64> + Into<u64> = varsig::encoding::Preset, | ||
> { | ||
ucans: BTreeMap<Cid, Delegation<DID, V, C>>, | ||
inner: Arc<RwLock<MemoryStoreInner<DID, V, C>>>, |
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.
A couple questions, oh wise Rustacean 🦀
Generality
I guess RwLock
here because it's the most general option? Is this a typical interface for libraries (as opposed to applications)? It really makes me want HKTs 😛
Deadlocks
From the std
docs:
The priority policy of the lock is dependent on the underlying operating system’s implementation, and this type does not guarantee that any particular policy will be used.
[...]
Potential deadlock example
That's... unsettling. Are there ways to guard against starvation other than "don't use a bad OS"?
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.
oh wise Rustacean 🦀
😂 🤦♂️ 😅
Deadlocks [...] That's... unsettling. Are there ways to guard against starvation other than "don't use a bad OS"?
It is unsettling! And you're wise in reading the documentation more than I have 😛
I chose RwLock because it should be faster than Mutex or equally fast in all cases, but admittedly, the deadlock potential on some OSes worries me, too. And performance shouldn't matter too much here, and if someone is worried about that, they could/should write a store implementation based on DashMap
.
Should we change it to Mutex?
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.
Reading up a bit I think I had some misconceptions about RwLocks. They can be slower than Mutexes in some cases. Some people recommend "Use Mutex unless you know what you're doing".
And I clearly don't! So let's switch to mutex 👍
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.
By the way -
Is this a typical interface for libraries (as opposed to applications)?
I'm not sure I understand. The RwLock/Mutex is totally an implementation detail and shouldn't leak to the interface. It's a private field.
The MemoryStore
/MemoryStoreInner
is a typical way of structuring code.
@@ -117,34 +162,39 @@ where | |||
delegation::Payload<DID>: TryFrom<Named<Ipld>>, | |||
Delegation<DID, V, Enc>: Encode<Enc>, | |||
{ | |||
type DelegationStoreError = String; // FIXME misisng | |||
type DelegationStoreError = Infallible; |
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.
Should we account for a poisoned RwLock
?
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.
I thought about this a bit. The poisoning is mostly important if you keep using a delegation store after it had panicked.
I don't think we have a reasonable way of restoring the invariants when it happens, or at least I don't think it's worth looking into that.
And given this is in the implementation of get_chain
:
let all_powerlines = read_tx.index.get(&None).unwrap_or(&blank_map);
let all_aud_for_subject = read_tx.index.get(subject).unwrap_or(&blank_map);
let powerline_candidates = all_powerlines.get(aud).unwrap_or(&blank_set);
let sub_candidates = all_aud_for_subject.get(aud).unwrap_or(&blank_set);
Broken invariants at least wouldn't cause further panics.
.map(|d| &d.payload) | ||
.collect(); | ||
.zip(invocation.proofs().iter()) | ||
.map(|(d, cid)| { |
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.
Perhaps worth making these pairs an option on the store?
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? I'm fine either way. Perhaps we just wait for a second case of this code happening, then we abstract?
@@ -290,6 +297,9 @@ pub enum ReceiveError< | |||
> where | |||
<S as Store<T, DID, V, C>>::InvocationStoreError: fmt::Debug, | |||
{ | |||
#[error("missing delegation: {0}")] | |||
MissingDelegation(Cid), |
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.
This is slightly ambiguous: is there a missing delegation in the prf
field vs from the store. Have you considered something like CannotFindDelegation(Cid)
?
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.
Yeah I like your suggestion!
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.
I took the freedom to adjust it to say DelegationNotFound
✌️
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.
Thanks again @matheus23! A couple questions for my own learning, but LGTM! 🚀
Ongoing discussion: https://talk.fission.codes/t/rs-ucan-1-0-api-notes/5347/5
delegation::Store::get
returnOption
delegation::Store::get
returnArc
sdelegation::Store::insert
take&self
instead of&mut self
invocation::Store