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

Review ben stephane #4

Closed
wants to merge 2 commits into from
Closed

Review ben stephane #4

wants to merge 2 commits into from

Conversation

smaldini
Copy link
Contributor

No description provided.

src/main/java/reactor/pool/AbstractPool.java Show resolved Hide resolved
src/main/java/reactor/pool/AffinityPool.java Show resolved Hide resolved
src/main/java/reactor/pool/AffinityPool.java Show resolved Hide resolved
@@ -43,6 +41,7 @@
*
* @return an ESTIMATED count of how many more resources can currently be allocated
*/
//TODO do we need to externalize metrics
Copy link
Member

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.

Copy link
Member

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.

Copy link
Member

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 🤔

Copy link
Member

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)
Copy link
Member

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.

Copy link
Member

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.

Copy link
Member

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;
Copy link
Member

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

Copy link
Member

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?

Copy link
Member

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");
Copy link
Member

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 ;)

src/main/java/reactor/pool/QueuePool.java Show resolved Hide resolved
@nebhale
Copy link
Member

nebhale commented Feb 14, 2019

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.

@nebhale
Copy link
Member

nebhale commented Feb 14, 2019

  • Pool Name (for .name())
  • Minsize, defaults to max size.

@smaldini
Copy link
Contributor Author

  • Internals review
  • Tier-0 pass on Serviceability
  • Fix Todo list
  • Fix external jctools use

@@ -27,7 +24,8 @@
*
* @author Simon Baslé
*/
public final class EvictionPredicates {
//TODO should be provided by Builder methods directly ?
Copy link
Member

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() {
Copy link
Member

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).

src/main/java/reactor/pool/PoolBuilder.java Show resolved Hide resolved
* {@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.
Copy link
Member

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;
Copy link
Member

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
Copy link
Member

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)

@simonbasle
Copy link
Member

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.

@smaldini
Copy link
Contributor Author

We'll continue on a new PR then - please let us merge the next ones or wait we continue the review next time 👍

@simonbasle simonbasle deleted the review-ben-stephane branch April 30, 2020 13:23
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

Successfully merging this pull request may close these issues.

None yet

3 participants