FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat: Implementation for batch dml in dbapi by ankiaga · Pull Request #1055 · googleapis/python-spanner · GitHub

This repository was archived by the owner on Jun 8, 2026. It is now read-only.
/ python-spanner Public archive

feat: Implementation for batch dml in dbapi - #1055

Merged
ankiaga merged 4 commits into
googleapis:mainfrom
ankiaga:batch_dml
Dec 14, 2023
Merged

feat: Implementation for batch dml in dbapi#1055
ankiaga merged 4 commits into
googleapis:mainfrom
ankiaga:batch_dml

Conversation

ankiaga commented Dec 12, 2023

Copy link
Copy Markdown
Contributor

No description provided.

ankiaga requested review from a team December 12, 2023 03:40
product-auto-label Bot added size: l Pull request size is large. api: spanner Issues related to the googleapis/python-spanner API. labels Dec 12, 2023
ankiaga added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Dec 12, 2023
yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Dec 12, 2023
and parsed_statement.statement_type != StatementType.INSERT
):
raise ProgrammingError(
"Only DML statements are allowed in batch " "DML mode."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

nit: can this be just one string instead of two concatenated strings?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Fixed

connection._transaction = None
raise Aborted(status.message)
elif status.code != OK:
raise OperationalError(status.message)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Should (could) this also include the status code?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Will take it in a follow up PR

self._read_only = value

@property
def batch_mode(self):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

If I understand it correctly, giving these names that do not start with an underscore will make them part of the public API. In that case, we should document them and also add validations to verify that they are only called with valid arguments. (But probably we should make them internal instead)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Removed the property

Args:
value (BatchMode)
"""
self._batch_mode = value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

What happens if an external uses calls this function when the connection is already in the middle of a different type of batch (e.g. it is now in a DML batch, and it is called to set it to DDL batch)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Removed this property

self._cursor.execute("start batch dml")
self._insert_row(7)
self._insert_row(8)
self._cursor.execute("run batch")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

It's probably difficult, but: Do we have any way that we could verify that the run batch call really sends an ExecuteBatchDml request, and does not execute multiple ExecuteSql requests sequentially?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Do you mean if we could assert something in the test for that?

Manually I have verified it by debugging

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Yes, I meant automated. That would then also guard us against any regressions, for example if something changes in the underlying client library, which cause this to use multiple ExecuteSql RPCs instead of one ExecuteBatchDml RPC.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

This needs some investigation, so will take it in a follow up PR

VALUES ({i}, 'first-name-{i}', 'last-name-{i}', 'test.email@domen.ru')
"""
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

We should also have tests for the unhappy path of using batch DML:

  1. What happens if the batch contains an invalid statement as the first statement?
  2. What happens if the batch contains an invalid statement in the middle?
  3. What happens if the batch contains an invalid statement at the end?
  4. Can we test and verify that the retry logic for Batch DML works as expected?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Added test for invalid statements.

There are issues with the retry logic which I am fixing in the next PR

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Ack

ankiaga added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Dec 14, 2023
yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Dec 14, 2023
ankiaga added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Dec 14, 2023
yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Dec 14, 2023

olavloite left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Looks generally good to me, but with one remaining question/nit on the exception that is raised when a batch fails. The exception should also contain the update counts of the statements that did succeed. Are we sure that it does?

self._cursor.execute("start batch dml")
self._insert_row(7)
self._insert_row(8)
self._cursor.execute("run batch")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Yes, I meant automated. That would then also guard us against any regressions, for example if something changes in the underlying client library, which cause this to use multiple ExecuteSql RPCs instead of one ExecuteBatchDml RPC.

"""
)
with pytest.raises(OperationalError):
self._cursor.execute("run batch")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I would have expected this to return a specific batch error that:

  1. Contains the error code, message etc.
  2. And contains the update counts of the statements that did succeed.

See for example this for the JDBC driver: https://github.com/googleapis/java-spanner-jdbc/blob/1f89f78c37b9e118e2c0cbc7f56d3eb1d5745863/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcPreparedStatementTest.java#L797

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

As discussed we need to create a custom exception class, so will take it in follow up PR

mock_db.run_in_transaction = mock_run_in = mock.MagicMock()
cursor.execute(sql="sql")
mock_run_in.assert_called_once_with(
cursor._do_execute_update_in_autocommit, "sql WHERE 1=1", None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Why did this change in this PR?

ankiaga Dec 14, 2023
edited
Loading

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

The logic to add WHERE clause has moved to parse_utils.classify_statement now which we are mocking here so its same as what is returned as per line 255

VALUES ({i}, 'first-name-{i}', 'last-name-{i}', 'test.email@domen.ru')
"""
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Ack

ankiaga enabled auto-merge (squash) December 14, 2023 17:17
ankiaga merged commit 7a92315 into googleapis:main Dec 14, 2023
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

api: spanner Issues related to the googleapis/python-spanner API. size: l Pull request size is large.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants


Back | FazBrowse Home | New Git URL