google-cloud-bigtable 2.46.0__tar.gz → 2.48.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {google_cloud_bigtable-2.46.0/google_cloud_bigtable.egg-info → google_cloud_bigtable-2.48.0}/PKG-INFO +1 -1
- google_cloud_bigtable-2.48.0/google/cloud/bigtable/batcher.py +224 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_async/_mutate_rows.py +1 -1
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_async/mutations_batcher.py +68 -18
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_helpers.py +66 -1
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_sync_autogen/_mutate_rows.py +1 -1
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_sync_autogen/mutations_batcher.py +57 -18
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/exceptions.py +14 -9
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/gapic_version.py +1 -1
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/table.py +12 -41
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin/gapic_version.py +1 -1
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/gapic_version.py +1 -1
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/types/table.py +33 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/gapic_version.py +1 -1
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0/google_cloud_bigtable.egg-info}/PKG-INFO +1 -1
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/data/test_system_async.py +36 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/data/test_system_autogen.py +29 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/v2_client/test_data_api.py +131 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_async/test_mutations_batcher.py +126 -2
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_sync_autogen/test_mutations_batcher.py +123 -2
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/test__helpers.py +88 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/gapic/bigtable_admin_v2/test_bigtable_table_admin.py +3 -0
- google_cloud_bigtable-2.48.0/tests/unit/v2_client/test_batcher.py +331 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_table.py +2 -2
- google_cloud_bigtable-2.46.0/google/cloud/bigtable/batcher.py +0 -462
- google_cloud_bigtable-2.46.0/tests/unit/v2_client/test_batcher.py +0 -350
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/LICENSE +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/MANIFEST.in +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/README.rst +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/app_profile.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/backup.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/cluster.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/column_family.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_async/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_async/_read_rows.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_async/_swappable_channel.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_async/client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_async/metrics_interceptor.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_cross_sync/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_cross_sync/_decorators.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_cross_sync/_mapping_meta.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_cross_sync/cross_sync.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_metrics/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_metrics/data_model.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_metrics/handlers/_base.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_metrics/handlers/gcp_exporter.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_metrics/handlers/opentelemetry.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_metrics/metrics_controller.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_metrics/tracked_retry.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_sync_autogen/_read_rows.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_sync_autogen/_swappable_channel.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_sync_autogen/client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_sync_autogen/metrics_interceptor.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/execute_query/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/execute_query/_async/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/execute_query/_async/execute_query_iterator.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/execute_query/_byte_cursor.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/execute_query/_checksum.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/execute_query/_parameters_formatting.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/execute_query/_query_result_parsing_utils.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/execute_query/_reader.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/execute_query/_sync_autogen/execute_query_iterator.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/execute_query/metadata.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/execute_query/values.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/mutations.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/read_modify_write_rules.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/read_rows_query.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/row.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/row_filters.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/encryption_info.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/enums.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/error.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/helpers.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/instance.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/policy.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/py.typed +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/row.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/row_data.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/row_filters.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/row_set.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin/py.typed +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/_compat.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/gapic_metadata.json +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/overlay/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/overlay/services/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/overlay/services/bigtable_table_admin/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/overlay/services/bigtable_table_admin/async_client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/overlay/services/bigtable_table_admin/client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/overlay/types/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/overlay/types/async_consistency.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/overlay/types/async_restore_table.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/overlay/types/consistency.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/overlay/types/restore_table.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/overlay/types/wait_for_consistency_request.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/py.typed +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/async_client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/pagers.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/base.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc_asyncio.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/rest.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/rest_base.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/async_client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/pagers.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/base.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc_asyncio.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/rest.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/rest_base.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/types/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/types/bigtable_instance_admin.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/types/bigtable_table_admin.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/types/common.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/types/instance.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/types/types.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/utils/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_admin_v2/utils/oneof_message.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/_compat.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/gapic_metadata.json +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/py.typed +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/services/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/services/bigtable/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/services/bigtable/async_client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/services/bigtable/client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/services/bigtable/transports/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/services/bigtable/transports/base.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/services/bigtable/transports/grpc.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/services/bigtable/transports/grpc_asyncio.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/services/bigtable/transports/rest.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/services/bigtable/transports/rest_base.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/types/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/types/bigtable.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/types/data.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/types/feature_flags.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/types/peer_info.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/types/request_stats.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/types/response_params.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/types/session.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable_v2/types/types.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google_cloud_bigtable.egg-info/SOURCES.txt +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google_cloud_bigtable.egg-info/dependency_links.txt +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google_cloud_bigtable.egg-info/not-zip-safe +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google_cloud_bigtable.egg-info/requires.txt +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google_cloud_bigtable.egg-info/top_level.txt +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/setup.cfg +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/setup.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/admin_overlay/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/admin_overlay/conftest.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/admin_overlay/test_system_async.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/admin_overlay/test_system_autogen.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/conftest.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/cross_sync/test_cases/async_to_sync.yaml +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/cross_sync/test_cases/cross_sync_files.yaml +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/cross_sync/test_cases/rm_aio.yaml +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/cross_sync/test_cases/strip_async_conditional_branches.yaml +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/cross_sync/test_cases/symbol_replacer.yaml +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/cross_sync/test_cross_sync_e2e.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/data/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/data/test_metrics_async.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/data/test_metrics_autogen.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/utils.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/v2_client/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/v2_client/_helpers.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/v2_client/conftest.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/v2_client/test_instance_admin.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/system/v2_client/test_table_admin.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/admin_overlay/my_oneof_message.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/admin_overlay/test_admin_packaging.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/admin_overlay/test_async_client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/admin_overlay/test_async_consistency.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/admin_overlay/test_async_restore_table.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/admin_overlay/test_client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/admin_overlay/test_consistency.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/admin_overlay/test_oneof_message.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/admin_overlay/test_restore_table.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/conftest.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_async/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_async/test__mutate_rows.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_async/test__read_rows.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_async/test__swappable_channel.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_async/test_client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_async/test_metrics_interceptor.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_async/test_read_rows_acceptance.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_cross_sync/test_cross_sync.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_cross_sync/test_cross_sync_decorators.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_metrics/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_metrics/test_data_model.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_metrics/test_gcp_exporter_handler.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_metrics/test_metrics_controller.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_metrics/test_opentelemetry_handler.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_metrics/test_tracked_retry.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_sync_autogen/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_sync_autogen/test__mutate_rows.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_sync_autogen/test__read_rows.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_sync_autogen/test__swappable_channel.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_sync_autogen/test_client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_sync_autogen/test_metrics_interceptor.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/_sync_autogen/test_read_rows_acceptance.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/_async/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/_async/test_query_iterator.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/_sync_autogen/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/_sync_autogen/test_query_iterator.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/resources/README.md +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/resources/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/resources/descriptors.pb +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/resources/singer.proto +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/resources/singer_pb2.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/sql_helpers.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/test_byte_cursor.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/test_checksum.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/test_execute_query_parameters_parsing.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/test_metadata.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/test_query_result_parsing_utils.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/execute_query/test_query_result_row_reader.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/read-rows-acceptance-test.json +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/test_exceptions.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/test_helpers.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/test_mutations.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/test_read_modify_write_rules.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/test_read_rows_query.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/test_row.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/test_row_filters.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/data/test_sync_up_to_date.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/gapic/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/gapic/bigtable_admin_v2/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/gapic/bigtable_admin_v2/test_bigtable_instance_admin.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/gapic/bigtable_admin_v2/test_compat.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/gapic/bigtable_v2/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/gapic/bigtable_v2/test_bigtable.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/gapic/bigtable_v2/test_compat.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/test_packaging.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/test_sql_routing_parameters.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/__init__.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/_testing.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/read-rows-acceptance-test.json +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_app_profile.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_backup.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_client.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_cluster.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_column_family.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_encryption_info.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_error.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_instance.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_policy.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_row.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_row_data.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_row_filters.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_row_merger.py +0 -0
- {google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/tests/unit/v2_client/test_row_set.py +0 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Copyright 2018 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
"""User friendly container for Google Cloud Bigtable MutationBatcher."""
|
|
17
|
+
|
|
18
|
+
import atexit
|
|
19
|
+
import queue
|
|
20
|
+
|
|
21
|
+
from google.api_core import exceptions as core_exceptions
|
|
22
|
+
|
|
23
|
+
from google.cloud.bigtable.data.exceptions import MutationsExceptionGroup
|
|
24
|
+
from google.cloud.bigtable.data.mutations import RowMutationEntry
|
|
25
|
+
|
|
26
|
+
FLUSH_COUNT = 100 # after this many elements, send out the batch
|
|
27
|
+
|
|
28
|
+
MAX_MUTATION_SIZE = 20 * 1024 * 1024 # 20MB # after this many bytes, send out the batch
|
|
29
|
+
|
|
30
|
+
MAX_OUTSTANDING_BYTES = 100 * 1024 * 1024 # 100MB # max inflight byte size.
|
|
31
|
+
|
|
32
|
+
MAX_OUTSTANDING_ELEMENTS = 100000 # max inflight mutations.
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class MutationsBatchError(Exception):
|
|
36
|
+
"""Error in the batch request"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, message, exc):
|
|
39
|
+
self.exc = exc
|
|
40
|
+
self.message = message
|
|
41
|
+
super().__init__(self.message)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class MutationsBatcher(object):
|
|
45
|
+
"""A MutationsBatcher is used in batch cases where the number of mutations
|
|
46
|
+
is large or unknown. It will store :class:`DirectRow` in memory until one of the
|
|
47
|
+
size limits is reached, the ``flush_interval`` timer fires, or an explicit call
|
|
48
|
+
to :func:`flush()` is performed. When a flush event occurs, the :class:`DirectRow`
|
|
49
|
+
in memory will be sent to Cloud Bigtable. Batching mutations is more efficient than
|
|
50
|
+
sending individual requests.
|
|
51
|
+
|
|
52
|
+
This class is not suited for usage in systems where each mutation
|
|
53
|
+
must be guaranteed to be sent, since calling :func:`mutate()` may only
|
|
54
|
+
result in an in-memory change. Rows are sent to the service when a size
|
|
55
|
+
limit is reached, when the ``flush_interval`` timer fires, when :func:`flush()`
|
|
56
|
+
is called explicitly, or when the batcher is closed (:func:`close()` is also
|
|
57
|
+
registered to run at interpreter exit). As a result, if the process
|
|
58
|
+
terminates abruptly -- e.g. a crash, ``SIGKILL``, or ``os._exit`` where the
|
|
59
|
+
``atexit`` handler never runs -- any :class:`DirectRow` still buffered in
|
|
60
|
+
memory is silently dropped and never sent, even after :func:`mutate()`
|
|
61
|
+
returned.
|
|
62
|
+
|
|
63
|
+
Note on thread safety: The same :class:`MutationBatcher` cannot be shared by multiple end-user threads.
|
|
64
|
+
|
|
65
|
+
.. warning::
|
|
66
|
+
|
|
67
|
+
If using ``MutationsBatcher``, please ensure you are using
|
|
68
|
+
``google-cloud-bigtable >= 2.42.0``, or consider migrating to the
|
|
69
|
+
batcher provided by the synchronous/asynchronous data client
|
|
70
|
+
(:mod:`google.cloud.bigtable.data`).
|
|
71
|
+
|
|
72
|
+
:type table: class
|
|
73
|
+
:param table: class:`~google.cloud.bigtable.table.Table`.
|
|
74
|
+
|
|
75
|
+
:type flush_count: int
|
|
76
|
+
:param flush_count: (Optional) Max number of rows to flush. If it
|
|
77
|
+
reaches the max number of rows it calls finish_batch() to mutate the
|
|
78
|
+
current row batch. Default is FLUSH_COUNT (1000 rows).
|
|
79
|
+
|
|
80
|
+
:type max_row_bytes: int
|
|
81
|
+
:param max_row_bytes: (Optional) Max number of row mutations size to
|
|
82
|
+
flush. If it reaches the max number of row mutations size it calls
|
|
83
|
+
finish_batch() to mutate the current row batch. Default is MAX_ROW_BYTES
|
|
84
|
+
(5 MB).
|
|
85
|
+
|
|
86
|
+
:type flush_interval: float
|
|
87
|
+
:param flush_interval: (Optional) Automatically flush every flush_interval seconds.
|
|
88
|
+
If None or <= 0, no time-based flushing is performed. Default is 1 second.
|
|
89
|
+
|
|
90
|
+
:type batch_completed_callback: Callable[list:[`~google.rpc.status_pb2.Status`]] = None
|
|
91
|
+
:param batch_completed_callback: (Optional) A callable for handling responses
|
|
92
|
+
after the current batch is sent. The callable function expect a list of grpc
|
|
93
|
+
Status.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def __init__(
|
|
97
|
+
self,
|
|
98
|
+
table,
|
|
99
|
+
flush_count=FLUSH_COUNT,
|
|
100
|
+
max_row_bytes=MAX_MUTATION_SIZE,
|
|
101
|
+
flush_interval=1,
|
|
102
|
+
batch_completed_callback=None,
|
|
103
|
+
):
|
|
104
|
+
self.table = table
|
|
105
|
+
self._batcher_kwargs = {
|
|
106
|
+
"flush_interval": flush_interval,
|
|
107
|
+
"flush_limit_mutation_count": flush_count,
|
|
108
|
+
"flush_limit_bytes": max_row_bytes,
|
|
109
|
+
"flow_control_max_mutation_count": MAX_OUTSTANDING_ELEMENTS,
|
|
110
|
+
"flow_control_max_bytes": MAX_OUTSTANDING_BYTES,
|
|
111
|
+
}
|
|
112
|
+
self._user_batch_completed_callback = batch_completed_callback
|
|
113
|
+
self._init_batcher()
|
|
114
|
+
atexit.register(self.close)
|
|
115
|
+
self._exceptions: queue.Queue = queue.Queue()
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def flush_count(self):
|
|
119
|
+
return self._batcher._flush_limit_count
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def max_row_bytes(self):
|
|
123
|
+
return self._batcher._flush_limit_bytes
|
|
124
|
+
|
|
125
|
+
def _init_batcher(self):
|
|
126
|
+
self._batcher = self.table._table_impl.mutations_batcher(**self._batcher_kwargs)
|
|
127
|
+
self._batcher._user_batch_completed_callback = (
|
|
128
|
+
self._user_batch_completed_callback
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
def _close_batcher(self):
|
|
132
|
+
try:
|
|
133
|
+
self._batcher.close()
|
|
134
|
+
except MutationsExceptionGroup as exc_group:
|
|
135
|
+
for error in exc_group.exceptions:
|
|
136
|
+
# Unpack the root cause of the FailedMutationEntryError and
|
|
137
|
+
# return that error to the user. In standard execution paths,
|
|
138
|
+
# FailedMutationEntryError always has an Exception cause;
|
|
139
|
+
# defensively wrap in Unknown if __cause__ is None.
|
|
140
|
+
if error.__cause__ is not None:
|
|
141
|
+
self._exceptions.put(error.__cause__)
|
|
142
|
+
else:
|
|
143
|
+
unknown_err = core_exceptions.Unknown(str(error))
|
|
144
|
+
unknown_err.__cause__ = error
|
|
145
|
+
self._exceptions.put(unknown_err)
|
|
146
|
+
except Exception as exc:
|
|
147
|
+
self._exceptions.put(exc)
|
|
148
|
+
|
|
149
|
+
def __enter__(self):
|
|
150
|
+
"""Starting the MutationsBatcher as a context manager"""
|
|
151
|
+
return self
|
|
152
|
+
|
|
153
|
+
def mutate(self, row):
|
|
154
|
+
"""Add a row to the batch. If the current batch meets one of the size
|
|
155
|
+
limits, the batch is sent asynchronously.
|
|
156
|
+
|
|
157
|
+
For example:
|
|
158
|
+
|
|
159
|
+
.. literalinclude:: snippets_table.py
|
|
160
|
+
:start-after: [START bigtable_api_batcher_mutate]
|
|
161
|
+
:end-before: [END bigtable_api_batcher_mutate]
|
|
162
|
+
:dedent: 4
|
|
163
|
+
|
|
164
|
+
:type row: class
|
|
165
|
+
:param row: :class:`~google.cloud.bigtable.row.DirectRow`.
|
|
166
|
+
|
|
167
|
+
:raises: One of the following:
|
|
168
|
+
* :exc:`~.table._BigtableRetryableError` if any row returned a transient error.
|
|
169
|
+
* :exc:`RuntimeError` if the number of responses doesn't match the number of rows that were retried
|
|
170
|
+
"""
|
|
171
|
+
self._batcher.append(RowMutationEntry(row.row_key, row._get_mutations()))
|
|
172
|
+
|
|
173
|
+
def mutate_rows(self, rows):
|
|
174
|
+
"""Add multiple rows to the batch. If the current batch meets one of the size
|
|
175
|
+
limits, the batch is sent asynchronously.
|
|
176
|
+
|
|
177
|
+
For example:
|
|
178
|
+
|
|
179
|
+
.. literalinclude:: snippets_table.py
|
|
180
|
+
:start-after: [START bigtable_api_batcher_mutate_rows]
|
|
181
|
+
:end-before: [END bigtable_api_batcher_mutate_rows]
|
|
182
|
+
:dedent: 4
|
|
183
|
+
|
|
184
|
+
:type rows: list:[`~google.cloud.bigtable.row.DirectRow`]
|
|
185
|
+
:param rows: list:[`~google.cloud.bigtable.row.DirectRow`].
|
|
186
|
+
|
|
187
|
+
:raises: One of the following:
|
|
188
|
+
* :exc:`~.table._BigtableRetryableError` if any row returned a transient error.
|
|
189
|
+
* :exc:`RuntimeError` if the number of responses doesn't match the number of rows that were retried
|
|
190
|
+
"""
|
|
191
|
+
for row in rows:
|
|
192
|
+
self.mutate(row)
|
|
193
|
+
|
|
194
|
+
def flush(self):
|
|
195
|
+
"""Sends the current batch to Cloud Bigtable synchronously.
|
|
196
|
+
For example:
|
|
197
|
+
|
|
198
|
+
.. literalinclude:: snippets_table.py
|
|
199
|
+
:start-after: [START bigtable_api_batcher_flush]
|
|
200
|
+
:end-before: [END bigtable_api_batcher_flush]
|
|
201
|
+
:dedent: 4
|
|
202
|
+
|
|
203
|
+
:raises:
|
|
204
|
+
* :exc:`~batcher.MutationsBatchError` if there's any error in the mutations.
|
|
205
|
+
"""
|
|
206
|
+
self._close_batcher()
|
|
207
|
+
self._init_batcher()
|
|
208
|
+
|
|
209
|
+
def __exit__(self, exc_type, exc_value, exc_traceback):
|
|
210
|
+
"""Clean up resources. Flush and shutdown the ThreadPoolExecutor."""
|
|
211
|
+
self.close()
|
|
212
|
+
|
|
213
|
+
def close(self):
|
|
214
|
+
"""Clean up resources. Flush and shutdown the ThreadPoolExecutor.
|
|
215
|
+
Any errors will be raised.
|
|
216
|
+
|
|
217
|
+
:raises:
|
|
218
|
+
* :exc:`~batcher.MutationsBatchError` if there's any error in the mutations.
|
|
219
|
+
"""
|
|
220
|
+
self._close_batcher()
|
|
221
|
+
atexit.unregister(self.close)
|
|
222
|
+
if self._exceptions.qsize() > 0:
|
|
223
|
+
exc = list(self._exceptions.queue)
|
|
224
|
+
raise MutationsBatchError("Errors in batch mutations.", exc=exc)
|
|
@@ -139,7 +139,7 @@ class _MutateRowsOperationAsync:
|
|
|
139
139
|
self._handle_entry_error(idx, exc)
|
|
140
140
|
finally:
|
|
141
141
|
# raise exception detailing incomplete mutations
|
|
142
|
-
all_errors: list[
|
|
142
|
+
all_errors: list[bt_exceptions.FailedMutationEntryError] = []
|
|
143
143
|
for idx, exc_list in self.errors.items():
|
|
144
144
|
if len(exc_list) == 0:
|
|
145
145
|
raise core_exceptions.ClientError(
|
|
@@ -16,15 +16,20 @@ from __future__ import annotations
|
|
|
16
16
|
|
|
17
17
|
import atexit
|
|
18
18
|
import concurrent.futures
|
|
19
|
+
import inspect
|
|
20
|
+
import logging
|
|
19
21
|
import time
|
|
20
22
|
import warnings
|
|
21
23
|
from collections import deque
|
|
22
|
-
from typing import TYPE_CHECKING, Sequence, cast
|
|
24
|
+
from typing import TYPE_CHECKING, Any, Callable, Sequence, cast
|
|
25
|
+
|
|
26
|
+
from google.rpc import code_pb2, status_pb2
|
|
23
27
|
|
|
24
28
|
from google.cloud.bigtable.data._cross_sync import CrossSync
|
|
25
29
|
from google.cloud.bigtable.data._helpers import (
|
|
26
30
|
TABLE_DEFAULT,
|
|
27
31
|
_get_retryable_errors,
|
|
32
|
+
_get_statuses_from_mutations_exception_group,
|
|
28
33
|
_get_timeouts,
|
|
29
34
|
)
|
|
30
35
|
from google.cloud.bigtable.data._metrics import ActiveOperationMetric, OperationType
|
|
@@ -54,6 +59,7 @@ __CROSS_SYNC_OUTPUT__ = "google.cloud.bigtable.data._sync_autogen.mutations_batc
|
|
|
54
59
|
|
|
55
60
|
# used to make more readable default values
|
|
56
61
|
_MB_SIZE = 1024 * 1024
|
|
62
|
+
_LOGGER = logging.getLogger(__name__)
|
|
57
63
|
|
|
58
64
|
|
|
59
65
|
@CrossSync.convert_class(sync_name="_FlowControl", add_mapping_for_name="_FlowControl")
|
|
@@ -290,10 +296,14 @@ class MutationsBatcherAsync:
|
|
|
290
296
|
self._exceptions_since_last_raise: int = 0
|
|
291
297
|
# keep track of the first and last _exception_list_limit exceptions
|
|
292
298
|
self._exception_list_limit: int = 10
|
|
293
|
-
self._oldest_exceptions: list[
|
|
294
|
-
self._newest_exceptions: deque[
|
|
299
|
+
self._oldest_exceptions: list[FailedMutationEntryError] = []
|
|
300
|
+
self._newest_exceptions: deque[FailedMutationEntryError] = deque(
|
|
295
301
|
maxlen=self._exception_list_limit
|
|
296
302
|
)
|
|
303
|
+
# only used by the shim right now.
|
|
304
|
+
self._user_batch_completed_callback: (
|
|
305
|
+
Callable[[list[status_pb2.Status]], Any] | None
|
|
306
|
+
) = None
|
|
297
307
|
# clean up on program exit
|
|
298
308
|
atexit.register(self._on_exit)
|
|
299
309
|
|
|
@@ -377,7 +387,11 @@ class MutationsBatcherAsync:
|
|
|
377
387
|
new_entries list of RowMutationEntry objects to flush
|
|
378
388
|
"""
|
|
379
389
|
# flush new entries
|
|
380
|
-
in_process_requests: list[
|
|
390
|
+
in_process_requests: list[
|
|
391
|
+
tuple[
|
|
392
|
+
CrossSync.Future[list[FailedMutationEntryError]], list[RowMutationEntry]
|
|
393
|
+
]
|
|
394
|
+
] = []
|
|
381
395
|
async for batch, metric in self._flow_control.add_to_flow_with_metrics(
|
|
382
396
|
new_entries, self._target.client._metrics
|
|
383
397
|
):
|
|
@@ -387,7 +401,7 @@ class MutationsBatcherAsync:
|
|
|
387
401
|
metric,
|
|
388
402
|
sync_executor=self._sync_rpc_executor,
|
|
389
403
|
)
|
|
390
|
-
in_process_requests.append(batch_task)
|
|
404
|
+
in_process_requests.append((batch_task, batch))
|
|
391
405
|
# wait for all inflight requests to complete
|
|
392
406
|
found_exceptions = await self._wait_for_batch_results(*in_process_requests)
|
|
393
407
|
# update exception data to reflect any new errors
|
|
@@ -410,6 +424,7 @@ class MutationsBatcherAsync:
|
|
|
410
424
|
list of FailedMutationEntryError objects for mutations that failed.
|
|
411
425
|
FailedMutationEntryError objects will not contain index information
|
|
412
426
|
"""
|
|
427
|
+
statuses = [status_pb2.Status(code=code_pb2.UNKNOWN) for _ in range(len(batch))]
|
|
413
428
|
try:
|
|
414
429
|
operation = CrossSync._MutateRowsOperation(
|
|
415
430
|
self._target.client._gapic_client,
|
|
@@ -422,16 +437,41 @@ class MutationsBatcherAsync:
|
|
|
422
437
|
)
|
|
423
438
|
await operation.start()
|
|
424
439
|
except MutationsExceptionGroup as e:
|
|
440
|
+
statuses = _get_statuses_from_mutations_exception_group(e, len(batch))
|
|
441
|
+
|
|
425
442
|
# strip index information from exceptions, since it is not useful in a batch context
|
|
426
443
|
for subexc in e.exceptions:
|
|
427
444
|
subexc.index = None
|
|
428
445
|
return list(e.exceptions)
|
|
446
|
+
else:
|
|
447
|
+
statuses = [status_pb2.Status(code=code_pb2.OK) for _ in range(len(batch))]
|
|
429
448
|
finally:
|
|
430
449
|
# mark batch as complete in flow control
|
|
431
450
|
await self._flow_control.remove_from_flow(batch)
|
|
451
|
+
|
|
452
|
+
# Call batch done callback with list of statuses.
|
|
453
|
+
# This is an internal callback used by the legacy synchronous shim,
|
|
454
|
+
# though async callbacks are awaited when running in async mode.
|
|
455
|
+
if self._user_batch_completed_callback:
|
|
456
|
+
try:
|
|
457
|
+
result = self._user_batch_completed_callback(statuses)
|
|
458
|
+
if CrossSync.is_async:
|
|
459
|
+
if inspect.isawaitable(result):
|
|
460
|
+
await result
|
|
461
|
+
else:
|
|
462
|
+
if inspect.isawaitable(result):
|
|
463
|
+
if inspect.iscoroutine(result):
|
|
464
|
+
result.close()
|
|
465
|
+
raise TypeError(
|
|
466
|
+
"_user_batch_completed_callback must be a synchronous callable"
|
|
467
|
+
)
|
|
468
|
+
except Exception as exc:
|
|
469
|
+
_LOGGER.warning(
|
|
470
|
+
f"Exception raised in user batch completion callback: {exc}"
|
|
471
|
+
)
|
|
432
472
|
return []
|
|
433
473
|
|
|
434
|
-
def _add_exceptions(self, excs: list[
|
|
474
|
+
def _add_exceptions(self, excs: list[FailedMutationEntryError]):
|
|
435
475
|
"""
|
|
436
476
|
Add new list of exceptions to internal store. To avoid unbounded memory,
|
|
437
477
|
the batcher will store the first and last _exception_list_limit exceptions,
|
|
@@ -531,26 +571,28 @@ class MutationsBatcherAsync:
|
|
|
531
571
|
@staticmethod
|
|
532
572
|
@CrossSync.convert
|
|
533
573
|
async def _wait_for_batch_results(
|
|
534
|
-
*tasks:
|
|
535
|
-
|
|
536
|
-
|
|
574
|
+
*tasks: tuple[
|
|
575
|
+
CrossSync.Future[list[FailedMutationEntryError]] | CrossSync.Future[None],
|
|
576
|
+
list[RowMutationEntry],
|
|
577
|
+
],
|
|
578
|
+
) -> list[FailedMutationEntryError]:
|
|
537
579
|
"""
|
|
538
580
|
Takes in a list of futures representing _execute_mutate_rows tasks,
|
|
539
581
|
waits for them to complete, and returns a list of errors encountered.
|
|
540
582
|
|
|
541
583
|
Args:
|
|
542
|
-
*tasks: futures representing _execute_mutate_rows or
|
|
584
|
+
*tasks: Tuples of futures representing _execute_mutate_rows or
|
|
585
|
+
_flush_internal tasks, and their associated batches
|
|
543
586
|
Returns:
|
|
544
|
-
list[
|
|
545
|
-
list of
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
output list. Successful tasks will not be represented in the output list.
|
|
587
|
+
list[FailedMutationEntryError]:
|
|
588
|
+
list of FailedMutationEntryError encountered by any of the tasks,
|
|
589
|
+
representing a failed mutation operation.
|
|
590
|
+
Successful tasks will not be represented in the output list.
|
|
549
591
|
"""
|
|
550
592
|
if not tasks:
|
|
551
593
|
return []
|
|
552
|
-
exceptions: list[
|
|
553
|
-
for task in tasks:
|
|
594
|
+
exceptions: list[FailedMutationEntryError] = []
|
|
595
|
+
for task, batch in tasks:
|
|
554
596
|
if CrossSync.is_async:
|
|
555
597
|
# futures don't need to be awaited in sync mode
|
|
556
598
|
await task
|
|
@@ -563,5 +605,13 @@ class MutationsBatcherAsync:
|
|
|
563
605
|
exc.index = None
|
|
564
606
|
exceptions.extend(exc_list)
|
|
565
607
|
except Exception as e:
|
|
566
|
-
exceptions.
|
|
608
|
+
exceptions.extend(
|
|
609
|
+
[
|
|
610
|
+
FailedMutationEntryError(
|
|
611
|
+
failed_idx=None, failed_mutation_entry=entry, cause=e
|
|
612
|
+
)
|
|
613
|
+
for entry in batch
|
|
614
|
+
]
|
|
615
|
+
)
|
|
616
|
+
|
|
567
617
|
return exceptions
|
{google_cloud_bigtable-2.46.0 → google_cloud_bigtable-2.48.0}/google/cloud/bigtable/data/_helpers.py
RENAMED
|
@@ -24,6 +24,7 @@ from typing import (
|
|
|
24
24
|
TYPE_CHECKING,
|
|
25
25
|
Callable,
|
|
26
26
|
List,
|
|
27
|
+
Optional,
|
|
27
28
|
Sequence,
|
|
28
29
|
Tuple,
|
|
29
30
|
Union,
|
|
@@ -32,8 +33,12 @@ from typing import (
|
|
|
32
33
|
from google.api_core import exceptions as core_exceptions
|
|
33
34
|
from google.api_core import retry as retries
|
|
34
35
|
from google.api_core.retry import RetryFailureReason, exponential_sleep_generator
|
|
36
|
+
from google.rpc import code_pb2, status_pb2
|
|
35
37
|
|
|
36
|
-
from google.cloud.bigtable.data.exceptions import
|
|
38
|
+
from google.cloud.bigtable.data.exceptions import (
|
|
39
|
+
MutationsExceptionGroup,
|
|
40
|
+
RetryExceptionGroup,
|
|
41
|
+
)
|
|
37
42
|
from google.cloud.bigtable.data.read_rows_query import ReadRowsQuery
|
|
38
43
|
|
|
39
44
|
if TYPE_CHECKING:
|
|
@@ -238,6 +243,66 @@ def _align_timeouts(operation: float, attempt: float | None) -> tuple[float, flo
|
|
|
238
243
|
return operation, final_attempt
|
|
239
244
|
|
|
240
245
|
|
|
246
|
+
def _get_statuses_from_mutations_exception_group(
|
|
247
|
+
exc_group: MutationsExceptionGroup, batch_size: int
|
|
248
|
+
) -> list[status_pb2.Status]:
|
|
249
|
+
"""
|
|
250
|
+
Helper function that populates a list of Status objects with exception information from
|
|
251
|
+
the exception group.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
exc_group: The exception group from a mutate rows operation
|
|
255
|
+
batch_size: How many RowMutationGroups were provided to the batch
|
|
256
|
+
Returns:
|
|
257
|
+
list[status_pb2.Status]: A list of Status proto objects
|
|
258
|
+
"""
|
|
259
|
+
# We exception handle as follows:
|
|
260
|
+
#
|
|
261
|
+
# 1. Each exception in the error group is a FailedMutationEntryError, and its
|
|
262
|
+
# cause is either a singular exception or a RetryExceptionGroup consisting of
|
|
263
|
+
# multiple exceptions.
|
|
264
|
+
#
|
|
265
|
+
# 2. In the case of a singular exception, if the error does not have a gRPC status
|
|
266
|
+
# code, we return a status code of UNKNOWN.
|
|
267
|
+
#
|
|
268
|
+
# 3. In the case of a RetryExceptionGroup, we use terminal exception in the exception
|
|
269
|
+
# group and process that.
|
|
270
|
+
statuses = [status_pb2.Status(code=code_pb2.OK) for _ in range(batch_size)]
|
|
271
|
+
for error in exc_group.exceptions:
|
|
272
|
+
if isinstance(error.index, int) and 0 <= error.index < len(statuses):
|
|
273
|
+
cause = error.__cause__
|
|
274
|
+
if isinstance(cause, RetryExceptionGroup):
|
|
275
|
+
statuses[error.index] = _get_status(cause.exceptions[-1])
|
|
276
|
+
else:
|
|
277
|
+
statuses[error.index] = _get_status(cause)
|
|
278
|
+
return statuses
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _get_status(exc: Optional[Exception]) -> status_pb2.Status:
|
|
282
|
+
"""
|
|
283
|
+
Helper function that returns a Status object corresponding to the given exception.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
exc: An exception to be converted into a Status.
|
|
287
|
+
Returns:
|
|
288
|
+
status_pb2.Status: A Status proto object.
|
|
289
|
+
"""
|
|
290
|
+
if (
|
|
291
|
+
isinstance(exc, core_exceptions.GoogleAPICallError)
|
|
292
|
+
and exc.grpc_status_code is not None
|
|
293
|
+
):
|
|
294
|
+
return status_pb2.Status( # type: ignore[unreachable]
|
|
295
|
+
code=exc.grpc_status_code.value[0],
|
|
296
|
+
message=exc.message,
|
|
297
|
+
details=exc.details,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
return status_pb2.Status(
|
|
301
|
+
code=code_pb2.UNKNOWN,
|
|
302
|
+
message=str(exc) if exc else "An unknown error has occurred",
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
|
|
241
306
|
def _validate_timeouts(
|
|
242
307
|
operation_timeout: float, attempt_timeout: float | None, allow_none: bool = False
|
|
243
308
|
):
|
|
@@ -112,7 +112,7 @@ class _MutateRowsOperation:
|
|
|
112
112
|
for idx in incomplete_indices:
|
|
113
113
|
self._handle_entry_error(idx, exc)
|
|
114
114
|
finally:
|
|
115
|
-
all_errors: list[
|
|
115
|
+
all_errors: list[bt_exceptions.FailedMutationEntryError] = []
|
|
116
116
|
for idx, exc_list in self.errors.items():
|
|
117
117
|
if len(exc_list) == 0:
|
|
118
118
|
raise core_exceptions.ClientError(
|
|
@@ -19,15 +19,20 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import atexit
|
|
21
21
|
import concurrent.futures
|
|
22
|
+
import inspect
|
|
23
|
+
import logging
|
|
22
24
|
import time
|
|
23
25
|
import warnings
|
|
24
26
|
from collections import deque
|
|
25
|
-
from typing import TYPE_CHECKING, Sequence, cast
|
|
27
|
+
from typing import TYPE_CHECKING, Any, Callable, Sequence, cast
|
|
28
|
+
|
|
29
|
+
from google.rpc import code_pb2, status_pb2
|
|
26
30
|
|
|
27
31
|
from google.cloud.bigtable.data._cross_sync import CrossSync
|
|
28
32
|
from google.cloud.bigtable.data._helpers import (
|
|
29
33
|
TABLE_DEFAULT,
|
|
30
34
|
_get_retryable_errors,
|
|
35
|
+
_get_statuses_from_mutations_exception_group,
|
|
31
36
|
_get_timeouts,
|
|
32
37
|
)
|
|
33
38
|
from google.cloud.bigtable.data._metrics import ActiveOperationMetric, OperationType
|
|
@@ -47,6 +52,7 @@ if TYPE_CHECKING:
|
|
|
47
52
|
)
|
|
48
53
|
from google.cloud.bigtable.data.mutations import RowMutationEntry
|
|
49
54
|
_MB_SIZE = 1024 * 1024
|
|
55
|
+
_LOGGER = logging.getLogger(__name__)
|
|
50
56
|
|
|
51
57
|
|
|
52
58
|
@CrossSync._Sync_Impl.add_mapping_decorator("_FlowControl")
|
|
@@ -255,10 +261,13 @@ class MutationsBatcher:
|
|
|
255
261
|
self._entries_processed_since_last_raise: int = 0
|
|
256
262
|
self._exceptions_since_last_raise: int = 0
|
|
257
263
|
self._exception_list_limit: int = 10
|
|
258
|
-
self._oldest_exceptions: list[
|
|
259
|
-
self._newest_exceptions: deque[
|
|
264
|
+
self._oldest_exceptions: list[FailedMutationEntryError] = []
|
|
265
|
+
self._newest_exceptions: deque[FailedMutationEntryError] = deque(
|
|
260
266
|
maxlen=self._exception_list_limit
|
|
261
267
|
)
|
|
268
|
+
self._user_batch_completed_callback: (
|
|
269
|
+
Callable[[list[status_pb2.Status]], Any] | None
|
|
270
|
+
) = None
|
|
262
271
|
atexit.register(self._on_exit)
|
|
263
272
|
|
|
264
273
|
def _timer_routine(self, interval: float | None) -> None:
|
|
@@ -326,7 +335,10 @@ class MutationsBatcher:
|
|
|
326
335
|
Args:
|
|
327
336
|
new_entries list of RowMutationEntry objects to flush"""
|
|
328
337
|
in_process_requests: list[
|
|
329
|
-
|
|
338
|
+
tuple[
|
|
339
|
+
CrossSync._Sync_Impl.Future[list[FailedMutationEntryError]],
|
|
340
|
+
list[RowMutationEntry],
|
|
341
|
+
]
|
|
330
342
|
] = []
|
|
331
343
|
for batch, metric in self._flow_control.add_to_flow_with_metrics(
|
|
332
344
|
new_entries, self._target.client._metrics
|
|
@@ -337,7 +349,7 @@ class MutationsBatcher:
|
|
|
337
349
|
metric,
|
|
338
350
|
sync_executor=self._sync_rpc_executor,
|
|
339
351
|
)
|
|
340
|
-
in_process_requests.append(batch_task)
|
|
352
|
+
in_process_requests.append((batch_task, batch))
|
|
341
353
|
found_exceptions = self._wait_for_batch_results(*in_process_requests)
|
|
342
354
|
self._entries_processed_since_last_raise += len(new_entries)
|
|
343
355
|
self._add_exceptions(found_exceptions)
|
|
@@ -355,6 +367,7 @@ class MutationsBatcher:
|
|
|
355
367
|
list[FailedMutationEntryError]:
|
|
356
368
|
list of FailedMutationEntryError objects for mutations that failed.
|
|
357
369
|
FailedMutationEntryError objects will not contain index information"""
|
|
370
|
+
statuses = [status_pb2.Status(code=code_pb2.UNKNOWN) for _ in range(len(batch))]
|
|
358
371
|
try:
|
|
359
372
|
operation = CrossSync._Sync_Impl._MutateRowsOperation(
|
|
360
373
|
self._target.client._gapic_client,
|
|
@@ -367,14 +380,30 @@ class MutationsBatcher:
|
|
|
367
380
|
)
|
|
368
381
|
operation.start()
|
|
369
382
|
except MutationsExceptionGroup as e:
|
|
383
|
+
statuses = _get_statuses_from_mutations_exception_group(e, len(batch))
|
|
370
384
|
for subexc in e.exceptions:
|
|
371
385
|
subexc.index = None
|
|
372
386
|
return list(e.exceptions)
|
|
387
|
+
else:
|
|
388
|
+
statuses = [status_pb2.Status(code=code_pb2.OK) for _ in range(len(batch))]
|
|
373
389
|
finally:
|
|
374
390
|
self._flow_control.remove_from_flow(batch)
|
|
391
|
+
if self._user_batch_completed_callback:
|
|
392
|
+
try:
|
|
393
|
+
result = self._user_batch_completed_callback(statuses)
|
|
394
|
+
if inspect.isawaitable(result):
|
|
395
|
+
if inspect.iscoroutine(result):
|
|
396
|
+
result.close()
|
|
397
|
+
raise TypeError(
|
|
398
|
+
"_user_batch_completed_callback must be a synchronous callable"
|
|
399
|
+
)
|
|
400
|
+
except Exception as exc:
|
|
401
|
+
_LOGGER.warning(
|
|
402
|
+
f"Exception raised in user batch completion callback: {exc}"
|
|
403
|
+
)
|
|
375
404
|
return []
|
|
376
405
|
|
|
377
|
-
def _add_exceptions(self, excs: list[
|
|
406
|
+
def _add_exceptions(self, excs: list[FailedMutationEntryError]):
|
|
378
407
|
"""Add new list of exceptions to internal store. To avoid unbounded memory,
|
|
379
408
|
the batcher will store the first and last _exception_list_limit exceptions,
|
|
380
409
|
and discard any in between.
|
|
@@ -453,24 +482,27 @@ class MutationsBatcher:
|
|
|
453
482
|
|
|
454
483
|
@staticmethod
|
|
455
484
|
def _wait_for_batch_results(
|
|
456
|
-
*tasks:
|
|
457
|
-
|
|
458
|
-
|
|
485
|
+
*tasks: tuple[
|
|
486
|
+
CrossSync._Sync_Impl.Future[list[FailedMutationEntryError]]
|
|
487
|
+
| CrossSync._Sync_Impl.Future[None],
|
|
488
|
+
list[RowMutationEntry],
|
|
489
|
+
],
|
|
490
|
+
) -> list[FailedMutationEntryError]:
|
|
459
491
|
"""Takes in a list of futures representing _execute_mutate_rows tasks,
|
|
460
492
|
waits for them to complete, and returns a list of errors encountered.
|
|
461
493
|
|
|
462
494
|
Args:
|
|
463
|
-
*tasks: futures representing _execute_mutate_rows or
|
|
495
|
+
*tasks: Tuples of futures representing _execute_mutate_rows or
|
|
496
|
+
_flush_internal tasks, and their associated batches
|
|
464
497
|
Returns:
|
|
465
|
-
list[
|
|
466
|
-
list of
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
output list. Successful tasks will not be represented in the output list."""
|
|
498
|
+
list[FailedMutationEntryError]:
|
|
499
|
+
list of FailedMutationEntryError encountered by any of the tasks,
|
|
500
|
+
representing a failed mutation operation.
|
|
501
|
+
Successful tasks will not be represented in the output list."""
|
|
470
502
|
if not tasks:
|
|
471
503
|
return []
|
|
472
|
-
exceptions: list[
|
|
473
|
-
for task in tasks:
|
|
504
|
+
exceptions: list[FailedMutationEntryError] = []
|
|
505
|
+
for task, batch in tasks:
|
|
474
506
|
try:
|
|
475
507
|
exc_list = task.result()
|
|
476
508
|
if exc_list:
|
|
@@ -478,5 +510,12 @@ class MutationsBatcher:
|
|
|
478
510
|
exc.index = None
|
|
479
511
|
exceptions.extend(exc_list)
|
|
480
512
|
except Exception as e:
|
|
481
|
-
exceptions.
|
|
513
|
+
exceptions.extend(
|
|
514
|
+
[
|
|
515
|
+
FailedMutationEntryError(
|
|
516
|
+
failed_idx=None, failed_mutation_entry=entry, cause=e
|
|
517
|
+
)
|
|
518
|
+
for entry in batch
|
|
519
|
+
]
|
|
520
|
+
)
|
|
482
521
|
return exceptions
|