Skip to content

Commit ae68de0

Browse files
authored
fix(spanner): update session bookkeeping for session NotFound (#15036)
1 parent 73f9031 commit ae68de0

File tree

6 files changed

+44
-14
lines changed

6 files changed

+44
-14
lines changed

CHANGELOG.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
breaking changes in the upcoming 3.x release. This release is scheduled for
55
2024-12 or 2025-01.
66

7-
## v2.22.1 - TBD
7+
## v2.22.1 - 2025-03
8+
9+
### [Spanner](/google/cloud/spanner/README.md)
10+
11+
- fix(spanner): update session bookkeeping for session NotFound (#15009)
812

913
## v2.22.0 - 2024-03
1014

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ project(
2323
google-cloud-cpp
2424
VERSION 2.22.1
2525
LANGUAGES CXX)
26-
set(PROJECT_VERSION_PRE_RELEASE "rc")
26+
set(PROJECT_VERSION_PRE_RELEASE "")
2727

2828
if (NOT "${PROJECT_VERSION_PRE_RELEASE}" STREQUAL "")
2929
set(PROJECT_VERSION "${PROJECT_VERSION}-${PROJECT_VERSION_PRE_RELEASE}")

google/cloud/internal/version_info.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121
#define GOOGLE_CLOUD_CPP_VERSION_MINOR 22
2222
// NOLINTNEXTLINE(modernize-macro-to-enum)
2323
#define GOOGLE_CLOUD_CPP_VERSION_PATCH 1
24-
#define GOOGLE_CLOUD_CPP_VERSION_PRE_RELEASE "rc"
24+
#define GOOGLE_CLOUD_CPP_VERSION_PRE_RELEASE ""
2525

2626
#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_VERSION_INFO_H

google/cloud/spanner/internal/session_pool.cc

+18-10
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ void SessionPool::Erase(std::string const& session_name) {
184184
target = std::move(session); // deferred deletion
185185
session = std::move(sessions_.back());
186186
sessions_.pop_back();
187+
DecrementSessionCount(lk, *target);
187188
break;
188189
}
189190
}
@@ -287,6 +288,21 @@ Status SessionPool::CreateSessions(
287288
return return_status;
288289
}
289290

291+
int SessionPool::total_sessions() const {
292+
std::lock_guard<std::mutex> lk(mu_);
293+
return total_sessions_;
294+
}
295+
296+
void SessionPool::DecrementSessionCount(
297+
std::unique_lock<std::mutex> const&,
298+
google::cloud::spanner_internal::Session const& session) {
299+
--total_sessions_;
300+
auto const& channel = session.channel();
301+
if (channel) {
302+
--channel->session_count;
303+
}
304+
}
305+
290306
StatusOr<SessionHolder> SessionPool::Allocate(bool dissociate_from_pool) {
291307
// We choose to ignore the internal::CurrentOptions() here as it is
292308
// non-deterministic when RPCs to create sessions are actually made.
@@ -299,11 +315,7 @@ StatusOr<SessionHolder> SessionPool::Allocate(bool dissociate_from_pool) {
299315
auto session = std::move(sessions_.back());
300316
sessions_.pop_back();
301317
if (dissociate_from_pool) {
302-
--total_sessions_;
303-
auto const& channel = session->channel();
304-
if (channel) {
305-
--channel->session_count;
306-
}
318+
DecrementSessionCount(lk, *session);
307319
}
308320
return {MakeSessionHolder(std::move(session), dissociate_from_pool)};
309321
}
@@ -367,11 +379,7 @@ void SessionPool::Release(std::unique_ptr<Session> session) {
367379
if (session->is_bad()) {
368380
// Once we have support for background processing, we may want to signal
369381
// that to replenish this bad session.
370-
--total_sessions_;
371-
auto const& channel = session->channel();
372-
if (channel) {
373-
--channel->session_count;
374-
}
382+
DecrementSessionCount(lk, *session);
375383
return;
376384
}
377385
session->update_last_use_time();

google/cloud/spanner/internal/session_pool.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@ class SessionPool : public std::enable_shared_from_this<SessionPool> {
9999
*/
100100
std::shared_ptr<SpannerStub> GetStub(Session const& session);
101101

102+
/**
103+
* Returns the number of sessions in the session pool plus the number of
104+
* sessions allocated to running transactions.
105+
*
106+
* @note This function should only be used for testing as other threads
107+
* could be modifying the underlying value immediately after it returns.
108+
*/
109+
int total_sessions() const;
110+
102111
private:
103112
friend std::shared_ptr<SessionPool> MakeSessionPool(
104113
spanner::Database, std::vector<std::shared_ptr<SpannerStub>>,
@@ -179,6 +188,10 @@ class SessionPool : public std::enable_shared_from_this<SessionPool> {
179188
// Remove the named session from the pool (if it is present).
180189
void Erase(std::string const& session_name);
181190

191+
// Performs the necessary bookkeeping when a session is removed from use.
192+
void DecrementSessionCount(std::unique_lock<std::mutex> const& lk,
193+
Session const& session);
194+
182195
spanner::Database const db_;
183196
google::cloud::CompletionQueue cq_;
184197
Options const opts_;
@@ -188,7 +201,7 @@ class SessionPool : public std::enable_shared_from_this<SessionPool> {
188201
int const max_pool_size_;
189202
std::mt19937 random_generator_;
190203

191-
std::mutex mu_;
204+
mutable std::mutex mu_;
192205
std::condition_variable cond_;
193206
std::vector<std::unique_ptr<Session>> sessions_; // GUARDED_BY(mu_)
194207
int total_sessions_ = 0; // GUARDED_BY(mu_)

google/cloud/spanner/internal/session_pool_test.cc

+5
Original file line numberDiff line numberDiff line change
@@ -601,21 +601,26 @@ TEST_F(SessionPoolTest, SessionRefreshNotFound) {
601601
// Wait for "s2" to need refreshing before releasing "s1".
602602
clock->AdvanceTime(keep_alive_interval * 2);
603603
}
604+
EXPECT_EQ(pool->total_sessions(), 2);
604605

605606
// Simulate completion of pending operations, which will result in
606607
// a call to RefreshExpiringSessions(). This should refresh "s2" and
607608
// satisfy the AsyncExecuteSql() expectation, which fails the call.
608609
impl->SimulateCompletion(true);
610+
EXPECT_EQ(pool->total_sessions(), 1);
609611

610612
// We should still be able to allocate session "s1".
611613
auto s1 = pool->Allocate();
612614
ASSERT_STATUS_OK(s1);
613615
EXPECT_EQ("s1", (*s1)->session_name());
616+
EXPECT_EQ(pool->total_sessions(), 1);
617+
614618
// However "s2" will be gone now, so a new allocation will produce
615619
// "s3", satisfying the final BatchCreateSessions() expectation.
616620
auto s3 = pool->Allocate();
617621
ASSERT_STATUS_OK(s3);
618622
EXPECT_EQ("s3", (*s3)->session_name());
623+
EXPECT_EQ(pool->total_sessions(), 2);
619624

620625
// Cancel all pending operations, satisfying any remaining futures. When
621626
// compiling with exceptions disabled the destructors eventually invoke

0 commit comments

Comments
 (0)