Add non-blocking (try_*) cache methods#112
Conversation
|
Let me know what you think, @arthurprs. Thank you in advance! |
non-blocking feature)
|
Thanks for the PR. In general I like the additions 👍 Do you think there's a practical benefit putting them behind the feature flag? |
There was a problem hiding this comment.
Pull request overview
Adds an opt-in non-blocking Cargo feature that exposes non-blocking cache operations, allowing callers to skip work when a shard lock is contended instead of blocking. This extends the existing synchronous cache API with contention-aware variants while keeping the default (blocking) behavior unchanged.
Changes:
- Introduces a
non-blockingfeature flag inCargo.toml. - Adds
try_read/try_writeto the internalRwLockwrapper (feature-gated). - Adds
ContendedResultplusCache::try_*methods that returnContendedon lock contention rather than blocking.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| Cargo.toml | Adds the non-blocking feature flag. |
| src/rw_lock.rs | Implements non-blocking try_read/try_write on the lock wrapper under the new feature. |
| src/sync.rs | Introduces ContendedResult and new Cache::try_* non-blocking operations gated by the feature. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Putting it under a feature flag for convenience, if you think it belongs in the default feature set, I have no objections. 😄 |
non-blocking feature)There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
try_*) cache methods
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// Attempts to acquire this `RwLock` with shared read access without blocking. | ||
| /// | ||
| /// Returns `Some(guard)` if the lock was acquired, or `None` if it is already | ||
| /// held by a writer. | ||
| #[inline] | ||
| pub fn try_read(&self) -> Option<RwLockReadGuard<'_, T>> { | ||
| #[cfg(feature = "parking_lot")] | ||
| { | ||
| self.0.try_read().map(RwLockReadGuard) | ||
| } | ||
| #[cfg(not(feature = "parking_lot"))] | ||
| { | ||
| match self.0.try_read() { | ||
| Ok(guard) => Some(RwLockReadGuard(guard)), | ||
| Err(std::sync::TryLockError::WouldBlock) => None, | ||
| Err(std::sync::TryLockError::Poisoned(err)) => panic!("{}", err), | ||
| } |
There was a problem hiding this comment.
The non-blocking lock APIs are documented/implemented here as panicking on poison, but when the crate is built with the shuttle feature it uses the RwLock wrapper in src/shim.rs, whose try_read/try_write currently convert all errors (including poisoning) into None. That means Cache::try_* can silently treat a poisoned lock as mere contention under shuttle. Align the shuttle implementation with these semantics (panic on poison, None only for WouldBlock) so behavior is consistent across feature sets.
There was a problem hiding this comment.
@arthurprs I think this is a valid suggestion, but it's outside the scope of this PR, wdyt?
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@arthurprs, if this is merged, can you also cut a release? |
|
Sorry, I've been unable to get to this. I'll cut a release after merged 👍 that may be during this weekend. |
Summary
try_read/try_writeto the internalRwLockwrapper, normalizingWouldBlocktoNoneand panicking on poison (matching the blocking variants' behavior).LockContentionerror type returned by all non-blocking operationswhen the target shard lock cannot be acquired immediately.
Cache:try_contains_key— returnsResult<bool, LockContention>try_get— returnsResult<Option<Val>, LockContention>try_peek— returnsResult<Option<Val>, LockContention>try_remove— returnsResult<Option<(Key, Val)>, LockContention>try_insert/try_insert_with_lifecycle— returnsResult<(), (Key, Val)>/Result<L::RequestState, (Key, Val)>(key+value returned on contention so thecaller can retry or discard)
Err(LockContention)on contention; write-path insertmethods return
Err((key, val))so the caller can retry or discard without losing data.Test plan
try_*method covering: hit, miss, and contended-lock scenarios.test_try_remove_contendedverifies the entry is still present after a failed non-blocking remove.test_try_insert_contendedverifies the key+value is returned to the caller on failure.cargo test— all tests pass.