-
Notifications
You must be signed in to change notification settings - Fork 32
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
Review ben stephane #4
Conversation
@@ -43,6 +41,7 @@ | |||
* | |||
* @return an ESTIMATED count of how many more resources can currently be allocated | |||
*/ | |||
//TODO do we need to externalize metrics |
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.
It's useful for metrics, but it is not a pure metrics concern. Eg. it is already useful to detect an unbounded strategy and chose the best Queue
implementation accordingly.
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, we caught that at the end, but left this in place because it's not clear that it should be used even for that. At issue is that you're creating a pattern that encourages a race condition which you fall afoul of.
The other way to look at this is as a code smell. At this time there are two different parties that need to know the max size of the pool; the allocation strategy and the pool. That means any changes to it probably need to be translated into two locations. This isn't textbook bad, but it does feel like it should be coalesced to a single authority.
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 AllocationStrategy
was introduced to replace a AtomicIntegerFieldUpdater LIVE
+ final int maxSize
cap. The estimatePermitCount
was intended as a way to provide an equivalent to LIVE.get()
, with the same limitations in terms of atomicity.
I don't think there is a race condition in the constructor, unless the AllocationStrategy
is reused (which it shouldn't be)? Nothing can call getPermit
until line 66 initSize
.
But maybe the method should be replaced with getPermitCap()
? More advanced strategies not purely based on a counter could still return Long.MAX_VALUE
to indicate that they have a different mode of granting permits 🤔
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.
Tracked in #7
@@ -40,6 +35,8 @@ | |||
* | |||
* @return the poolable | |||
*/ | |||
//TODO Evaluate if poolable could just be "get" (standard in the JVM reference | |||
// containers) |
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 don't really like the *Reference#get()
API style in the JDK, but that's personal preference I guess. I think a more "business wording" style is better. compare Predicate<T>#test
vs Function<T,Boolean>#apply
.
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 recommended the change, but not strongly. My personal preferences go towards the descriptive names, but there's an argument about preferring to be Java-idiomatic. If you care strongly, I'm happy to defer.
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.
Tracked in #11
@@ -0,0 +1,60 @@ | |||
package reactor.pool; |
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.
see discussion above about re-merging that with PooledRef
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.
As per second round of review, I'm finding what I'm strongly opposed to is to make these metrics "optional" and have instanceof
checks in eviction predicates, but it can be less visible (hidden behind one level of indirection, PooledRef#metadata()
).
Maybe the term metadata
is more accurate than metrics
, if we later want to add some feature-specific state?
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.
Tracked in #11
for (int i = 0; i < initSize; i++) { | ||
long start = metricsRecorder.now(); | ||
try { | ||
POOLABLE poolable = Objects.requireNonNull(poolConfig.allocator().block(), "allocator returned null in constructor"); | ||
//FIXME NO! | ||
POOLABLE poolable = Objects.requireNonNull(poolConfig.allocator.block(), "allocator returned null in constructor"); |
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.
see same discussion in AffinityPool
;)
Just as an FYI, I don't think any of this code (especially the mistaken javadocs and outstanding todos) are anywhere near merging. Its just what we got to last night. |
|
|
@@ -27,7 +24,8 @@ | |||
* | |||
* @author Simon Baslé | |||
*/ | |||
public final class EvictionPredicates { | |||
//TODO should be provided by Builder methods directly ? |
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.
Mmh ok you're right, obvious examples of direct usage of these predicate in these simple forms are not easy to come by...
I was thinking about a case where you'd want to evict a connection based on age because the configuration used for initialization might periodically change in a non-critical way (ie it is ok to continue using the connection, but preferable to recreate one to accomodate the configuration). But even then, it would probably not be something based purely on age...
Ok, let's do with idle
only (since its the most obvious and regular use case), on the builder along with a generic roll-your-own Predicate
based option.
* | ||
* @return this {@link Pool} builder | ||
*/ | ||
public PoolBuilder<T> unboundedSize() { |
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.
Ok 👍
Would reverting the suffix to a prefix make sense though? sizeUnbounded()
vs sizeMax(int)
? This way the 2 choices are exposed side by side in autocompletion. (and they would need a @see
entry pointing to allocationStrategy
, since they overwrite each other).
* {@link #acquireCount() acquired}, the {@link Pool} may optionally use that information to automatically invalidate | ||
* the object, and provide a simplified acquire mechanism where the {@link PooledRef} is not directly exposed (see | ||
* {@link Pool#acquireInScope(Function)} vs {@link Pool#acquire()}). | ||
* manually {@link #release()} it to the pool or {@link #invalidate()} it. |
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.
Although I expect manual invalidation via invalidate()
would largely occur upon detecting failures (like an SQL error code or similar), it can't be excluded that these "metrics" might be useful in triggering a decision to invalidate()
.
But I think you have a point with the noise aspect. Why not then keep a middle ground:
interface PooledRefMetrics {
int acquisitionCount();
long ageMillis();
long idleMillis();
//...
}
interface PooledRef<T> {
release();
//...
PooledRefMetrics metrics(); //avoids instanceof
}
And have the concrete pooled ref implement both interfaces to limit garbage:
AbstractPooledRef<T> implements PooledRef<T>, PooledRefMetrics { ... }
@@ -0,0 +1,60 @@ | |||
package reactor.pool; |
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.
As per second round of review, I'm finding what I'm strongly opposed to is to make these metrics "optional" and have instanceof
checks in eviction predicates, but it can be less visible (hidden behind one level of indirection, PooledRef#metadata()
).
Maybe the term metadata
is more accurate than metrics
, if we later want to add some feature-specific state?
* | ||
* @return the number of times this object has been used by consumers of the pool | ||
*/ | ||
//TODO do we need that for a start |
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.
It is easy to compute, we need to mark acquisition to ensure the idleTime()
one is correctly reset to 0 anyway, so why not have it (especially if it is less in the user's face)
I think I'll integrate most of the changes we agree on here as separate commits on the master branch, as the format of this PR makes it hard to split in discrete chunks (the package reorg kills it for me). I'll also open issues for items still being discussed. |
We'll continue on a new PR then - please let us merge the next ones or wait we continue the review next time 👍 |
No description provided.