Title: [Spanner] TransactionManager.close() not idempotent; API doc example is broken · Issue #3172 · googleapis/google-cloud-java · GitHub
Open Graph Title: [Spanner] TransactionManager.close() not idempotent; API doc example is broken · Issue #3172 · googleapis/google-cloud-java
X Title: [Spanner] TransactionManager.close() not idempotent; API doc example is broken · Issue #3172 · googleapis/google-cloud-java
Description: I am using the new TransactionManager API and am getting mysterious "rollback can only be called if the transaction is in progress" exceptions. I think I've tracked down the problem... The API docs example shown with DatabaseClient.trans...
Open Graph Description: I am using the new TransactionManager API and am getting mysterious "rollback can only be called if the transaction is in progress" exceptions. I think I've tracked down the problem... The API docs...
X Description: I am using the new TransactionManager API and am getting mysterious "rollback can only be called if the transaction is in progress" exceptions. I think I've tracked down the problem.....
Opengraph URL: https://github.com/googleapis/google-cloud-java/issues/3172
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"[Spanner] TransactionManager.close() not idempotent; API doc example is broken","articleBody":"I am using the new `TransactionManager` API and am getting mysterious `\"rollback can only be called if the transaction is in progress\"` exceptions.\r\n\r\nI think I've tracked down the problem...\r\n\r\nThe API docs example shown with `DatabaseClient.transactionManager()` is this:\r\n```\r\n long singerId = my_singer_id;\r\n try (TransactionManager manager = dbClient.transactionManager()) {\r\n TransactionContext txn = manager.begin();\r\n while (true) {\r\n String column = \"FirstName\";\r\n Struct row = txn.readRow(\"Singers\", Key.of(singerId), Collections.singleton(column));\r\n String name = row.getString(column);\r\n txn.buffer(\r\n Mutation.newUpdateBuilder(\"Singers\").set(column).to(name.toUpperCase()).build());\r\n try {\r\n manager.commit();\r\n break;\r\n } catch (AbortedException e) {\r\n Thread.sleep(e.getRetryDelayInMillis() / 1000);\r\n txn = manager.resetForRetry();\r\n }\r\n }\r\n }\r\n```\r\nHowever, this example usage is actually wrong.\r\n\r\nThe reason is that the implementation returned from `DatabaseClient.transactionManager()` is actually an `AutoClosingTransactionManager` (in `SessionPool.java`), which automatically `close()`s the transaction if `commit()` or `rollback()` is invoked. Sounds like a good idea, right?\r\n\r\nThe problem is that `AutoClosingTransactionManager.close()` is not idempotent:\r\n```\r\n @Override\r\n public void close() {\r\n try {\r\n delegate.close();\r\n } finally {\r\n session.close();\r\n }\r\n }\r\n```\r\nIf the transaction is closed twice, then the second time around the `session` is closed _again_, even though it has already been returned to the pool and possibly reassigned to another transaction. If so, this in turn causes that other transaction to fail on `commit()` with an exception `\"rollback can only be called if the transaction is in progress\"`.\r\n\r\nOne fix for this is to document that a `TransactionManager` must only be closed exactly once, and fix your API javadoc example accordingly.\r\n\r\nA much better and more robust fix would be to fix `AutoClosingTransactionManager.close()` to be idempotent. `TransactionManager` could then implement `Closeable` instead of `AutoCloseable` to denote this (if desired).","author":{"url":"https://github.com/archiecobbs","@type":"Person","name":"archiecobbs"},"datePublished":"2018-04-19T18:56:42.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":4},"url":"https://github.com/3172/google-cloud-java/issues/3172"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:9e4b4e22-5e7c-58d5-c62c-dc757b18c23d |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | DF66:2495A6:D6A915:1367102:6A4CC861 |
| html-safe-nonce | 10699f9b7e1fd54bb18e72464e6d4183081c681f4f84f196699ad4f54f2475a7 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJERjY2OjI0OTVBNjpENkE5MTU6MTM2NzEwMjo2QTRDQzg2MSIsInZpc2l0b3JfaWQiOiI3OTE0NjIzNDE1Njg2Nzc5ODUiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | dee7692fb87140cb10661e3b58195840635ac174cda937da78c0eea382b54031 |
| hovercard-subject-tag | issue:315999721 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/googleapis/google-cloud-java/3172/issue_layout |
| twitter:image | https://opengraph.githubassets.com/dd3bdb529c7dac9be0cbfd3cc477850ec2854354391cc29b83cf55b65499d6ea/googleapis/google-cloud-java/issues/3172 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/dd3bdb529c7dac9be0cbfd3cc477850ec2854354391cc29b83cf55b65499d6ea/googleapis/google-cloud-java/issues/3172 |
| og:image:alt | I am using the new TransactionManager API and am getting mysterious "rollback can only be called if the transaction is in progress" exceptions. I think I've tracked down the problem... The API docs... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | archiecobbs |
| hostname | github.com |
| expected-hostname | github.com |
| None | 3d11bb817438277de2a940854450e83a7d32b6aeb5014e9e6b00a6423900251c |
| turbo-cache-control | no-preview |
| go-import | github.com/googleapis/google-cloud-java git https://github.com/googleapis/google-cloud-java.git |
| octolytics-dimension-user_id | 16785467 |
| octolytics-dimension-user_login | googleapis |
| octolytics-dimension-repository_id | 26181278 |
| octolytics-dimension-repository_nwo | googleapis/google-cloud-java |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 26181278 |
| octolytics-dimension-repository_network_root_nwo | googleapis/google-cloud-java |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | d0da0eb92994395299ed4450bf67b0373005be36 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width