Title: Bigtable: read_rows: no deadline causing stuck clients; no way to change it · Issue #6 · googleapis/python-bigtable · GitHub
Open Graph Title: Bigtable: read_rows: no deadline causing stuck clients; no way to change it · Issue #6 · googleapis/python-bigtable
X Title: Bigtable: read_rows: no deadline causing stuck clients; no way to change it · Issue #6 · googleapis/python-bigtable
Description: Table.read_rows does not set any deadline, so it can hang forever if the Bigtable server connection hangs. We see this happening once every week or two when running inside GCP, which causes our server to get stuck indefinitely. There app...
Open Graph Description: Table.read_rows does not set any deadline, so it can hang forever if the Bigtable server connection hangs. We see this happening once every week or two when running inside GCP, which causes our ser...
X Description: Table.read_rows does not set any deadline, so it can hang forever if the Bigtable server connection hangs. We see this happening once every week or two when running inside GCP, which causes our ser...
Opengraph URL: https://github.com/googleapis/python-bigtable/issues/6
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Bigtable: read_rows: no deadline causing stuck clients; no way to change it","articleBody":"`Table.read_rows` does not set any deadline, so it can hang forever if the Bigtable server connection hangs. We see this happening once every week or two when running inside GCP, which causes our server to get stuck indefinitely. There appears to be no way in the API to set a deadline, even though [the documentation](https://googleapis.github.io/google-cloud-python/latest/bigtable/table.html#google.cloud.bigtable.table.Table.read_rows) says that the `retry` parameter should do this. Due to a bug, it does not.\r\n\r\n### Details:\r\n\r\nWe are calling `Table.read_rows` to read ~2 rows from BigTable. Using pyflame on a stuck process, both worker threads were waiting on Bigtable, with the stack trace below. I believe the bug is the following:\r\n\r\n1. Call `Table.read_rows`. [This calls `PartialRowsData`](https://github.com/googleapis/google-cloud-python/blob/master/bigtable/google/cloud/bigtable/table.py#L436), passing the `retry` argument which defaults to `DEFAULT_RETRY_READ_ROWS`. The default misleadingly sets `deadline=60.0`. ; It also passes `read_method=self._instance._client.table_data_client.transport.read_rows` to `PartialRowsData`, which is a method on `BigtableGrpcTransport`.\r\n2. [`PartialRowsData.__init__` calls `read_method()`](https://github.com/googleapis/google-cloud-python/blob/master/bigtable/google/cloud/bigtable/row_data.py#L398); this is actually raw gRPC `_UnaryStreamMultiCallable`, *not* the gapic `BigtableClient.read_rows`, which AFAICS, is never called. Hence, this gRPC streaming call is started with any deadline. \r\n3. `PartialRowsData.__iter__` calls `self._read_next_response`, which [calls `return self.retry(self._read_next, on_error=self._on_error)()`](https://github.com/googleapis/google-cloud-python/blob/master/bigtable/google/cloud/bigtable/row_data.py#L454). This gives the impression that `retry` is used, but if I understand gRPC streams correctly, I'm not sure that even makes sense. I think even if the gRPC stream return some error, calling `next` won't actually retry the gRPC, it will just immediately raise the same exception. To retry, I believe you need to actually restart it by calling `read_rows` again.\r\n4. If the Bigtable server now \"hangs\", the client hangs forever.\r\n\r\n### Possible fix:\r\n\r\nChange `Table.read_rows` call the gapic `BigtableClient.read_rows` with the `retry` parameter., and change `PartialRowsData.__init__` to take this response iterator, and not take a `retry` parameter at all. This would at least allow setting the gRPC streaming call deadline, although I don't think it will make retrying work (since I *think* the gRPC streaming client will just immediately returns an iterator without actually waiting for a response from the server?)\r\n\r\nI haven't actually tried implementing this to see if it works. For now, we will probably just make a raw gRPC read_rows call so we can set an appropriate timeout. \r\n\r\n\r\n#### Environment details\r\n\r\n*OS*: Linux, ContainerOS (GKE), Container is Debian9 (using distroless)\r\n*Python*: 3.5.3\r\n*API*: google-cloud-bigtable 0.33.0\r\n\r\n\r\n#### Steps to reproduce\r\n\r\nThis program loads the Bigtable emulator with 1000 rows, calls `read_rows(retry=DEFAULT.with_deadline(5.0))`, then sends `SIGSTOP` to pause the emulator. This SHOULD cause a `DeadlineExceeded` exception to be raised after 5 seconds. Instead, it hangs forever.\r\n\r\n1. Start the Bigtable emulator: `gcloud beta emulators bigtable start`\r\n2. Find the PID: `ps ax | grep cbtemulator`\r\n3. Run the following program with `BIGTABLE_EMULATOR_HOST=localhost:8086 python3 bug.py $PID`\r\n\r\n```python\r\nfrom google.api_core import exceptions\r\nfrom google.cloud import bigtable\r\nfrom google.rpc import code_pb2\r\nfrom google.rpc import status_pb2\r\nimport os\r\nimport signal\r\nimport sys\r\n\r\nCOLUMN_FAMILY_ID = 'column_family_id'\r\n\r\ndef main():\r\n emulator_pid = int(sys.argv[1])\r\n client = bigtable.Client(project=\"testing\", admin=True)\r\n instance = client.instance(\"emulator\")\r\n\r\n # create/open a table\r\n table = instance.table(\"emulator_table\")\r\n column_family = table.column_family(COLUMN_FAMILY_ID)\r\n try:\r\n table.create()\r\n column_family.create()\r\n except exceptions.AlreadyExists:\r\n print('table exists')\r\n\r\n # write a bunch of data\r\n for i in range(1000):\r\n k = 'some_key_{:04d}'.format(i)\r\n print(k)\r\n row = table.row(k)\r\n row.set_cell(COLUMN_FAMILY_ID, 'column', 'some_value{:d}'.format(i) * 1000)\r\n result = table.mutate_rows([row])\r\n assert len(result) == 1 and result[0].code == code_pb2.OK\r\n assert table.read_row(k) is not None\r\n\r\n print('starting read')\r\n rows = table.read_rows(retry=bigtable.table.DEFAULT_RETRY_READ_ROWS.with_deadline(5.0))\r\n rows_iter = iter(rows)\r\n r1 = next(rows_iter)\r\n print('read', r1)\r\n os.kill(emulator_pid, signal.SIGSTOP)\r\n print('sent sigstop')\r\n for r in rows_iter:\r\n print(r)\r\n print('done')\r\n\r\n\r\nif __name__ == '__main__':\r\n main()\r\n```\r\n\r\n#### Stack trace of hung server (using slightly older version of the google-cloud-bigtable library\r\n\r\n```\r\n/usr/local/lib/python2.7/threading.py:wait:340\r\n/usr/local/lib/python2.7/site-packages/grpc/_channel.py:_next:348\r\n/usr/local/lib/python2.7/site-packages/grpc/_channel.py:next:366\r\n/usr/local/lib/python2.7/site-packages/google/cloud/bigtable/row_data.py:_read_next:426\r\n/usr/local/lib/python2.7/site-packages/google/api_core/retry.py:retry_target:179\r\n/usr/local/lib/python2.7/site-packages/google/api_core/retry.py:retry_wrapped_func:270\r\n/usr/local/lib/python2.7/site-packages/google/cloud/bigtable/row_data.py:_read_next_response:430\r\n/usr/local/lib/python2.7/site-packages/google/cloud/bigtable/row_data.py:__iter__:441\r\n```\r\n","author":{"url":"https://github.com/evanj","@type":"Person","name":"evanj"},"datePublished":"2019-05-29T00:22:56.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":6},"url":"https://github.com/6/python-bigtable/issues/6"}
| 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:f1c6188c-1489-ac4d-c652-a856a189abe5 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 9DCA:3B16A6:2CD6CB4:3E1E3FA:6A4F0A53 |
| html-safe-nonce | dcad8fa1126ec0a6f193c164f647f78d309180161d26fc653d3441bed945b212 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5RENBOjNCMTZBNjoyQ0Q2Q0I0OjNFMUUzRkE6NkE0RjBBNTMiLCJ2aXNpdG9yX2lkIjoiNTYwMTAzNDMxMzgyNDkzMDM4NyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | eabc1527add357606cdbdc5b455bbaf7d9d9e61a0dfb8af11b8deb7e063d37d0 |
| hovercard-subject-tag | issue:558431526 |
| 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/python-bigtable/6/issue_layout |
| twitter:image | https://opengraph.githubassets.com/f64f3d656b90e21eb6950ca6feec392c650e85a61578e5215c9aa19513641591/googleapis/python-bigtable/issues/6 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/f64f3d656b90e21eb6950ca6feec392c650e85a61578e5215c9aa19513641591/googleapis/python-bigtable/issues/6 |
| og:image:alt | Table.read_rows does not set any deadline, so it can hang forever if the Bigtable server connection hangs. We see this happening once every week or two when running inside GCP, which causes our ser... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | evanj |
| hostname | github.com |
| expected-hostname | github.com |
| None | b92d11c0aa4a77d54ef4af1078b6a15fb5a70a215b30c4ecf28889d5a8e656d9 |
| turbo-cache-control | no-preview |
| go-import | github.com/googleapis/python-bigtable git https://github.com/googleapis/python-bigtable.git |
| octolytics-dimension-user_id | 16785467 |
| octolytics-dimension-user_login | googleapis |
| octolytics-dimension-repository_id | 226992487 |
| octolytics-dimension-repository_nwo | googleapis/python-bigtable |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 226992487 |
| octolytics-dimension-repository_network_root_nwo | googleapis/python-bigtable |
| 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 | 2b8f23afb982271f1b22258a94aede67a6b77760 |
| ui-target | canary-2 |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width