Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quietly prepare unprepared query #806

Merged
merged 2 commits into from
Sep 7, 2023
Merged

Conversation

sylwiaszunejko
Copy link
Contributor

@sylwiaszunejko sylwiaszunejko commented Sep 1, 2023

Motivation

See the referenced issue (#800)

What's done

Information about the types of the bind markers is necessary to make the serialization API safe (i.e. so that the user data won't be misinterpreted when sending it to the database). The problem with unprepared statements is that the driver doesn't have a good way to determine column names and types of the bind markers.

Now before sending an unprepared statement, it is quietly prepared on one connection to obtain the information about the bind markers. If the list of values to be bound is empty, we just send it as an unprepared query.

Evaluation

Integration tests are added.

Refs: #800

Pre-review checklist

  • I have split my patch into logically separate commits.
  • All commit messages clearly explain what they change and why.
  • I added relevant tests for new features and bug fixes.
  • All commits compile, pass static checks and pass test.
  • PR description sums up the changes and reasons why they should be introduced.
  • I have provided docstrings for the public items that I want to introduce.
  • I have adjusted the documentation in ./docs/source/.
  • I added appropriate Fixes: annotations to PR description.

@sylwiaszunejko sylwiaszunejko changed the title Prepare query Quietly prepare unprepared query Sep 1, 2023
// Needed to avoid moving query and values into async move block
let query_ref = &query;
let values_ref = &serialized_values;
let paging_state_ref = &paging_state;
let values_size = values.serialized().unwrap().into_owned().size();
Copy link
Collaborator

Choose a reason for hiding this comment

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

.serialized() is an operation that can be potentially costly. It is already computed on line 624, so I suggest using the already computed serialized_values instead.

Comment on lines 666 to 669
let serial_consistency = prepared
.config
.serial_consistency
.unwrap_or(default_consistency);
Copy link
Collaborator

Choose a reason for hiding this comment

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

The prepared statement will have exactly the same config as the original query object, so there is no need to calculate serial consistency again.

Copy link
Collaborator

@piodul piodul left a comment

Choose a reason for hiding this comment

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

Apart from moving the preparation into Connection::query, we will also have to handle batches... Batches can consist of multiple statements, both prepared and unprepared. I guess we have to update Connection::batch as well to handle unprepared statements with arguments. We can start with preparing them sequentially in that function, or with some low (fixed?) concurrency.

Comment on lines 661 to 675
let values_size = serialized_values.size();
async move {
if values_size != 0 {
let prepared = connection.prepare(query_ref).await?;
return connection
.execute_with_consistency(
&prepared,
values_ref,
consistency,
serial_consistency,
paging_state_ref.clone(),
)
.await
.and_then(QueryResponse::into_non_error_query_response);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I just realized that maybe it's not the best place for fixing the issue. There is the Connection::query function which, while not being a public API, is used internally by some of our code, and we would like to benefit from better safety there. I think it is better to call prepare in Connection::query directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So maybe we should call prepare in Connection::query_with_consistency, because both Session::query and Connection::query use that.

Before sending an unprepared statement, quietly prepare it on one connection to obtain the information about the
bind markers. If the list of values to be bound is empty, we
just send it as an unprepared query.
@piodul piodul merged commit 5d27034 into scylladb:main Sep 7, 2023
piodul added a commit that referenced this pull request Oct 5, 2023
This reverts commit 5d27034, reversing
changes made to 6f0dcfd.
piodul added a commit that referenced this pull request Oct 5, 2023
This reverts commit 5d27034, reversing
changes made to 6f0dcfd.
piodul added a commit that referenced this pull request Oct 6, 2023
This reverts commit 5d27034, reversing
changes made to 6f0dcfd.
@Lorak-mmk Lorak-mmk mentioned this pull request Dec 14, 2023
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants