Skip to content
This repository has been archived by the owner on Dec 8, 2021. It is now read-only.

feat: associate SpannerStub with a Session #1041

Merged
merged 4 commits into from
Nov 13, 2019
Merged

feat: associate SpannerStub with a Session #1041

merged 4 commits into from
Nov 13, 2019

Conversation

mr-salty
Copy link
Contributor

@mr-salty mr-salty commented Nov 12, 2019

SessionPool manages assigning a SpannerStub to each Session
ConnectionImpl obtains the stub to use for a call from the Session
and no longer retains any knowledge of the stub.

This will enable the use of multiple stubs per Connection; it's
important for a Session to remain associated with the stub/channel
that created it, otherwise there is a performance penalty.

Part of #307


This change is Reviewable

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Nov 12, 2019
@mr-salty
Copy link
Contributor Author


google/cloud/spanner/internal/connection_impl.cc, line 518 at r1 (raw file):

  auto const& stub = session->stub();

FYI, I suspect the const& here does nothing now; I was thinking something like the compiler might see that it's doing lifetime extension and can effectively std::move it into the lambda capture, but I guess if it's smart enough to figure that out it could also figure it out if it was just a value. So, should I delete it?

Is there any way to accomplish the goal of not doing an extra ref/unref other than having Session return a shared_ptr<>&? I didn't want to do that because it seems dangerous.

Does just doing session->stub()->Call() now bump the reference count as well or can that be optimized away? I did some googling but couldn't really find anything. I didn't play with godbolt but I guess I could try that.

@codecov
Copy link

codecov bot commented Nov 12, 2019

Codecov Report

Merging #1041 into master will decrease coverage by 0.03%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1041      +/-   ##
==========================================
- Coverage   94.82%   94.78%   -0.04%     
==========================================
  Files         158      160       +2     
  Lines       10610    10628      +18     
==========================================
+ Hits        10061    10074      +13     
- Misses        549      554       +5
Impacted Files Coverage Δ
google/cloud/spanner/internal/session_pool.cc 93.26% <100%> (+0.26%) ⬆️
google/cloud/spanner/internal/session_pool_test.cc 99.16% <100%> (+0.04%) ⬆️
google/cloud/spanner/internal/connection_impl.cc 96.31% <100%> (+0.04%) ⬆️
google/cloud/spanner/internal/session.h 100% <100%> (ø) ⬆️
google/cloud/spanner/internal/session.cc 100% <100%> (ø)
...loud/spanner/internal/partial_result_set_resume.cc 90.9% <0%> (-4.55%) ⬇️
...on_tests/rpc_failure_threshold_integration_test.cc 85.55% <0%> (-2.23%) ⬇️
google/cloud/spanner/internal/spanner_stub.cc 66.66% <0%> (-2.23%) ⬇️
google/cloud/spanner/samples/samples.cc 89.42% <0%> (-0.24%) ⬇️
...anner/integration_tests/client_integration_test.cc 99.45% <0%> (-0.01%) ⬇️
... and 5 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7d43196...157f6a4. Read the comment docs.

google/cloud/spanner/internal/session.h Outdated Show resolved Hide resolved
google/cloud/spanner/internal/session_pool_test.cc Outdated Show resolved Hide resolved
google/cloud/spanner/transaction.cc Outdated Show resolved Hide resolved
google/cloud/spanner/internal/connection_impl.cc Outdated Show resolved Hide resolved
google/cloud/spanner/internal/session_pool.cc Outdated Show resolved Hide resolved
Copy link
Contributor

@coryan coryan left a comment

Choose a reason for hiding this comment

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

Reviewed 6 of 8 files at r1.
Reviewable status: 6 of 8 files reviewed, 6 unresolved discussions (waiting on @coryan, @devbww, @devjgm, @mr-salty, and @scotthart)


google/cloud/spanner/internal/connection_impl.cc, line 296 at r1 (raw file):

 * an error if `session` is empty and no `Session` can be allocated.
 */
Status ConnectionImpl::PrepareSession(SessionHolder& session,

Consider moving this function to SessionPool, calling it PrepareSessionForCall() and have it return a StatusOr<std::pair<Session, Stub>>. I think the association between sessions and stubs should be a thing that only the session pool knows about.


google/cloud/spanner/internal/connection_impl.cc, line 518 at r1 (raw file):

Does just doing session->stub()->Call() now bump the reference count as well or can that be optimized away?

I would say it does incr/decr the refcount. There is nothing, for example, that might stop a concurrent "session->clear_stub()" call from happening between the return of stub() and the call of Call().

That is true, but safe. The dangerous thing is a call to session->clear_stub() before a call to session->stub()->Call() that is a crash.


google/cloud/spanner/internal/session_pool.cc, line 145 at r1 (raw file):

Previously, devbww (Bradley White) wrote…

Does it make any additional sense to move this logic into Session. That is, session->AssignStubIfNeeded(stub_)?

+1 to that.

`SessionPool` manages assigning a `SpannerStub` to each `Session`
`ConnectionImpl` obtains the stub to use for a call from the `Session`
and no longer retains any knowledge of the stub.

This will enable the use of multiple stubs per `Connection`; it's
important for a `Session` to remain associated with the stub/channel
that created it, otherwise there is a performance penalty.

Part of #307
Copy link
Contributor Author

@mr-salty mr-salty left a comment

Choose a reason for hiding this comment

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

Reviewable status: 6 of 8 files reviewed, 6 unresolved discussions (waiting on @coryan, @devbww, @devjgm, and @scotthart)


google/cloud/spanner/transaction.cc, line 100 at r1 (raw file):

Previously, devbww (Bradley White) wrote…

At the moment it doesn't look like MakeDissociatedSessionHolder() really needs to be a template. Might it just take a std::string session_name and then be able to provide the nullptr to the Session ctor itself?

Done.


google/cloud/spanner/internal/connection_impl.cc, line 296 at r1 (raw file):

Previously, coryan (Carlos O'Ryan) wrote…

Consider moving this function to SessionPool, calling it PrepareSessionForCall() and have it return a StatusOr<std::pair<Session, Stub>>. I think the association between sessions and stubs should be a thing that only the session pool knows about.

I like this idea but I think it ends up being a little weird because of the in/out nature of the SessionHolder and it not being copyable.


google/cloud/spanner/internal/connection_impl.cc, line 518 at r1 (raw file):

Previously, coryan (Carlos O'Ryan) wrote…

Does just doing session->stub()->Call() now bump the reference count as well or can that be optimized away?

I would say it does incr/decr the refcount. There is nothing, for example, that might stop a concurrent "session->clear_stub()" call from happening between the return of stub() and the call of Call().

That is true, but safe. The dangerous thing is a call to session->clear_stub() before a call to session->stub()->Call() that is a crash.

I think Brad was saying that if I changed stub() to return a reference to the shared_ptr instead of returning it by value (which seems to be the only way to prevent this extra refcount bump/decrement - is that something we really care about avoiding?), then a clear_stub() call (or another set_stub()) would be a problem.

In reality returning a reference would still be "safe" because there is no clear_stub() and set_stub() will only be called once, before any call to stub(). Although, I'd argue that returning a reference to a smart pointer is inherently an unsafe API. I really didn't want to have set_stub() at all, then it's clear the stub is set in the constructor and never changes. bww and I tossed around some ideas about that yesterday, but playing around with them they seemed strictly worse, requiring adding extra methods on Connection or extra members to *Params structs. The way it's done in this PR effectively keeps all of that contained in internal namespace things, at the expense of awkward (internal) APIs like this.

I still feel like there is probably a better solution, but also felt like I spun my wheels on this long enough. Carlos' suggestion above about returning the stub from a SessionPool method would be nice because I could then make the stub-related methods on Session private with SessionPool as a friend. But, it does always bump the reference count.

I've been mulling the idea of SpannerStub* Sesson::operator->() but I don't know if we want to go down that path. It also doesn't work with the streaming calls where we're capturing the stub, although if Brad goes through with his plan to make Session refcounted we could capture the shared_ptr<Session> instead.

Anyways, I left this as-is for now but I'll have a NYChove with Carlos.


google/cloud/spanner/internal/session.h, line 47 at r1 (raw file):

Previously, devbww (Bradley White) wrote…

move(stub)?

Done.


google/cloud/spanner/internal/session_pool.cc, line 145 at r1 (raw file):

Previously, coryan (Carlos O'Ryan) wrote…

+1 to that.

I think it's better here, because this is eventually going to be something like:

if (session->stub() == nullptr) {
  // some logic to pick a stub from a set
  // increment the count of sessions using that stub
  session->set_stub(chosen_stub);
}

google/cloud/spanner/internal/session_pool_test.cc, line 244 at r1 (raw file):

Previously, devbww (Bradley White) wrote…

What does this replay purport to test?

Just that the stub hasn't changed across calls.

instead of permanently assigning a stub to a session that was born of a
partition, just return one that can be used (since the call should only
happen once).
Copy link
Contributor Author

@mr-salty mr-salty left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 of 9 files reviewed, 6 unresolved discussions (waiting on @coryan, @devbww, @devjgm, and @scotthart)


google/cloud/spanner/internal/connection_impl.cc, line 296 at r1 (raw file):

Previously, mr-salty (Todd Derr) wrote…

I like this idea but I think it ends up being a little weird because of the in/out nature of the SessionHolder and it not being copyable.

I split GetStub to another call so we can get it closer to where it's used... I'm ambivalent about whether the original function should live here or in SessionPool so I'm inclined to leave it here for now unless you feel strongly.


google/cloud/spanner/internal/connection_impl.cc, line 518 at r1 (raw file):

Previously, mr-salty (Todd Derr) wrote…

I think Brad was saying that if I changed stub() to return a reference to the shared_ptr instead of returning it by value (which seems to be the only way to prevent this extra refcount bump/decrement - is that something we really care about avoiding?), then a clear_stub() call (or another set_stub()) would be a problem.

In reality returning a reference would still be "safe" because there is no clear_stub() and set_stub() will only be called once, before any call to stub(). Although, I'd argue that returning a reference to a smart pointer is inherently an unsafe API. I really didn't want to have set_stub() at all, then it's clear the stub is set in the constructor and never changes. bww and I tossed around some ideas about that yesterday, but playing around with them they seemed strictly worse, requiring adding extra methods on Connection or extra members to *Params structs. The way it's done in this PR effectively keeps all of that contained in internal namespace things, at the expense of awkward (internal) APIs like this.

I still feel like there is probably a better solution, but also felt like I spun my wheels on this long enough. Carlos' suggestion above about returning the stub from a SessionPool method would be nice because I could then make the stub-related methods on Session private with SessionPool as a friend. But, it does always bump the reference count.

I've been mulling the idea of SpannerStub* Sesson::operator->() but I don't know if we want to go down that path. It also doesn't work with the streaming calls where we're capturing the stub, although if Brad goes through with his plan to make Session refcounted we could capture the shared_ptr<Session> instead.

Anyways, I left this as-is for now but I'll have a NYChove with Carlos.

ok, after discussing it with the team, Session::stub_ is now const but can still be nullptr. However, the only way to obtain the stub is by calling SessionPool::GetStub(session) which returns a SpannerStub for one-time use.

Copy link
Contributor

@coryan coryan left a comment

Choose a reason for hiding this comment

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

:lgtm: but you should wait for @devbww

Reviewed 9 of 10 files at r2.
Reviewable status: all files reviewed, 5 unresolved discussions (waiting on @devbww, @devjgm, and @scotthart)

@@ -44,7 +45,11 @@ class Session {
std::string const& session_name() const { return session_name_; }

private:
std::string session_name_;
friend class SessionPool; // for access to stub()
Copy link
Contributor

Choose a reason for hiding this comment

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

PS: Is it worth it to try to hide stub()?

Copy link
Contributor Author

@mr-salty mr-salty left a comment

Choose a reason for hiding this comment

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

Reviewable status: all files reviewed, 6 unresolved discussions (waiting on @devbww, @devjgm, and @scotthart)


google/cloud/spanner/internal/session.h, line 48 at r2 (raw file):

Previously, devbww (Bradley White) wrote…

PS: Is it worth it to try to hide stub()?

I wanted to force callers to use SessionPool::GetStub() instead of calling this. As we discussed in chat I think I should also make the constructor private, but I'll do that in a separate PR.

@mr-salty mr-salty merged commit d0e133a into googleapis:master Nov 13, 2019
@mr-salty mr-salty deleted the stub-from-session branch November 13, 2019 16:45
devjgm pushed a commit to devjgm/google-cloud-cpp that referenced this pull request May 7, 2020
…ud-cpp-spanner#1041)

*`SessionPool` manages assigning a `SpannerStub` to each `Session`
*`ConnectionImpl` obtains the stub to use for a call from the `SessionPool` and no longer retains any knowledge of the stub.

This will enable the use of multiple stubs per `Connection`; it's important for a `Session` to remain associated with the stub/channel that created it, otherwise there is a performance penalty.

Part of googleapis/google-cloud-cpp-spanner#307
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants