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

Add max wait for connection borrowing in pool manager #6387

Closed

Conversation

danicheg
Copy link
Member

Closes http4s/blaze#654. It's better to review with hidden whitespaces. The impl is super straightforward, and I could be missing something stupidly obvious.

@mergify mergify bot added series/0.23 PRs targeting 0.23.x module:blaze-client labels May 17, 2022
pool <- mkPool(
maxTotal = 1,
maxIdleDuration = Duration.Inf,
maxBorrowDuration = 2.seconds,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anecdotally says, but at local, this test-suite takes about 0.001s on execution. However, at CI it may become flaky. But still, I didn't mark it as 'flaky' before receiving any facts about its flakiness.

Copy link
Member Author

@danicheg danicheg May 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lovely. It turned out that the other test suite became flaky.
mehh

Comment on lines 220 to 224
F.delay(
logger.warn(s"Requesting connection for $key has exceed timeout")
) *>
decrConnection(randKey)
} *>
createConnection(key, callback)
else
F.delay(
logger.debug(
s"No connections available for the desired key, $key. Adding to waitQueue: $stats"
)
) *>
addToWaitQueue(key, callback)

case None => // we're full up. Add to waiting queue.
F.delay(
logger.debug(
s"No connections available for $key. Waiting on new connection: $stats"
)
) *>
addToWaitQueue(key, callback)
}

F.delay(logger.debug(s"Requesting connection for $key: $stats")).productR(go()).as(None)
} else
F.delay(callback(Left(new IllegalStateException("Connection pool is closed")))).as(None)
}
F.delay(callback(Left(ConnectionBorrowingException(key))))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the only reason this can get canceled? I'm trying to reason about when we'd see this message instead of the timeout on line 311, and whether the message (and callback invocation) is accurate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh-oh, you're 100% right. Thanks for pointing that out. For sure, canceling could be caused by various reasons. But do you hint that we shouldn't invoke the callback here? I feel we can't be too sure if CE will resolve the asynchronous deadlock via the standard canceling mechanism smoothly. According to timeoutTo scaladoc:

"If the source is uncancelable, the resulting effect will wait for it to complete before evaluating the fallback."

So I've tried to soften the blow.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what would make that source uncancelable. And if it is, instead of never completing, won't it just ignore the borrow? It seems you're partially reinventing timeoutTo.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the 'cancelation in CE3' tutorial by Daniel, I'm sure I was wrong and actually was reinventing the wheel with Ref here, as you pointed out (if someone is interested, link to the discussion https://discord.com/channels/632277896739946517/632278585700384799/977634027651608626).
I think a few hot-takes should be highlighted. Also, this answers the question 'what makes the source uncancelable':

  1. async is uncancelable during registration;
  2. if one defines async without cancel token (i. e. using None instead of actual effect), then async will be uncancellable;
  3. things created via delay is uncancellable too.

So summing up, I'm not sure that introducing timeout via timeoutTo will work as expected all the time. I think that for sure could be scenarios when

  1. the whole async effect will be considered uncancelable,
  2. and timeoutTo will wait for the natural execution of async then produce an exception (for timeout limit exceeding case).

And I don't think that what we actually want is that behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. This never returns:

IO.uncancelable(_ => IO.never)
  .timeoutTo(1.second, IO.unit)
  .unsafeRunSync()

@armanbilge
Copy link
Member

No rush, FYI this is a blocker for blaze schism :)

@armanbilge armanbilge mentioned this pull request May 20, 2022
@rossabaker
Copy link
Member

I can recreate this in blaze if we have to.

@rossabaker
Copy link
Member

I think I'll port this one to the blaze repo.

@danicheg
Copy link
Member Author

Superseded by http4s/blaze#681.

@danicheg danicheg closed this May 25, 2022
@danicheg danicheg deleted the blaze-client-pool-manager branch May 25, 2022 17:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Consider a max wait in pool manager
3 participants