spanner: Fixed session leakage for ApplyAtLeastOnce

Previously session handles where leaked whenever Commit() returned a
non-abort, non-session-not-found error, due to a missing recycle() call.

Fixes #1776.

Change-Id: I45839d3258f96bf6d1dfc15c35e6e146117d70f1
Reviewed-on: https://code-review.googlesource.com/c/gocloud/+/51890
Reviewed-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Shanika Kuruppu <skuruppu@google.com>
Reviewed-by: Knut Olav Løite <koloite@gmail.com>
diff --git a/spanner/session.go b/spanner/session.go
index 9a3dfae..301135c 100644
--- a/spanner/session.go
+++ b/spanner/session.go
@@ -130,6 +130,11 @@
 func (sh *sessionHandle) destroy() {
 	sh.mu.Lock()
 	s := sh.session
+	if s == nil {
+		// sessionHandle has already been recycled.
+		sh.mu.Unlock()
+		return
+	}
 	tracked := sh.trackedSessionHandle
 	sh.session = nil
 	sh.trackedSessionHandle = nil
@@ -137,10 +142,6 @@
 	sh.stack = nil
 	sh.mu.Unlock()
 
-	if s == nil {
-		// sessionHandle has already been destroyed..
-		return
-	}
 	if tracked != nil {
 		p := s.pool
 		p.mu.Lock()
diff --git a/spanner/transaction.go b/spanner/transaction.go
index b02a220..a42916b 100644
--- a/spanner/transaction.go
+++ b/spanner/transaction.go
@@ -1018,6 +1018,7 @@
 				// creations/retrivals.
 				return ts, err
 			}
+			defer sh.recycle()
 		}
 		res, err := sh.getClient().Commit(contextWithOutgoingMetadata(ctx, sh.getMetadata()), &sppb.CommitRequest{
 			Session: sh.getID(),
@@ -1043,9 +1044,6 @@
 			break
 		}
 	}
-	if sh != nil {
-		sh.recycle()
-	}
 	return ts, toSpannerError(err)
 }