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

sql: transaction_read_only can be reset in AOST txns #44200

Closed
knz opened this issue Jan 22, 2020 · 16 comments · Fixed by #120097
Closed

sql: transaction_read_only can be reset in AOST txns #44200

knz opened this issue Jan 22, 2020 · 16 comments · Fixed by #120097
Assignees
Labels
A-sql-optimizer SQL logical planning and optimizations. C-bug Code not up to spec/doc, specs & docs deemed correct. Solution expected to change code/behavior. S-3-erroneous-edge-case Database produces or stores erroneous data without visible error/warning, in rare edge cases. T-sql-foundations SQL Foundations Team (formerly SQL Schema + SQL Sessions)

Comments

@knz
Copy link
Contributor

knz commented Jan 22, 2020

The following works and should not:

BEGIN AS OF SYSTEM TIME 'sometime in the past';
SET transaction_read_only = false;
CREATE TABLE t(x INT);
INSERT INTO t(x) VALUES (1);
SELECT * FROM t;

This succeeds and lets the transaction do stuff with the table !!?!?!

It also leaves over junk data in ranges. I haven't checked but I suspect it also leaves junk data at historical timestamps which will confuse the GC (and possibly let node crashes when table readers get their table data removed from "under them" asynchronously by a confused GC.

I am expecting an error on the SET: "cannot set a txn read/write when operating at a past timestamp"

Found while investigating #44188 (comment)

Jira issue: CRDB-5245

@knz knz added C-bug Code not up to spec/doc, specs & docs deemed correct. Solution expected to change code/behavior. A-sql-optimizer SQL logical planning and optimizations. labels Jan 22, 2020
@knz knz added the S-3-erroneous-edge-case Database produces or stores erroneous data without visible error/warning, in rare edge cases. label Jan 22, 2020
@knz
Copy link
Contributor Author

knz commented Jan 22, 2020

@andreimatei can you have a look at the above. This is a serious issue.

@knz
Copy link
Contributor Author

knz commented Jan 22, 2020

I think the durable fix is to push the readOnly field down into client.Txn and simply reject any KV request that writes if the client.Txn is read-only.

@knz
Copy link
Contributor Author

knz commented Jan 22, 2020

cc @cockroachdb/sql-execution

@andreimatei
Copy link
Contributor

I've looked superficially and it seems to me that at least the INSERT should be rejected by this code:

if opt.IsMutationOp(e) && b.evalCtx.TxnReadOnly {

We seem to be plumbing evalCtx.TxnReadOnly fine as far as I can see. Except obviously something is broken.

I'm reticent to add a read-only mode to client.Txn too quickly. I'm not sure that it'd exactly what SQL needs and I think it might cause us problems later on. For example, should a SQL read-only txn be allowed to increment sequences or acquire table leases? The answers to these questions should be independent on which transaction ends up being used to perform those operations. Conversely, should a read-only client.Txn be allowed to assist with garbage collection of old values that it scans over, as we've talked about doing?
If this read-only thing can remain exclusively a SQL concern, I'd much rather leave it there. An example of a time we've added something similar to KV only to quickly regret it was the transaction's "request count".

I'll pass this over to @jordanlewis to look into.

@knz
Copy link
Contributor Author

knz commented Jan 23, 2020

We seem to be plumbing evalCtx.TxnReadOnly fine as far as I can see. Except obviously something is broken.

What is broken is that the SET transaction_read_only is happy to set the value from true to false when it's non-sensical to do so.

For reference

  • PostgreSQL requires the session var to be togglable back and forth. So at least the SET code is morally doing what it's supposed to do
  • the real problem is that AOST transactions are read-only in a way that's non-negotiable. We definitely can't increment SQL sequences inside of them, nor acquire table leases under them (but note that lease acquisition is typically done under a separate txn exactly for this reason already)

@knz
Copy link
Contributor Author

knz commented Jan 23, 2020

My recommendation above to have a "read only" flag inside client.Txn was limited to AOST queries (i.e. not to SQL clients asking transaction_read_only=false after it started read/write). That is, after a call is made to (client.Txn).SetFixedTimestamp, any KV mutation operation should be forbidden. That seems like it falls squarely on KV's shoulders.

@tbg mind chiming in for advice?

@andreimatei
Copy link
Contributor

That is, after a call is made to (client.Txn).SetFixedTimestamp, any KV mutation operation should be forbidden.

Why? What's wrong with writing after calling SetFixedTimestamp? Schema change txns used to do exactly that (I think more recently they stopped using SetFixedTimestamp()).

@knz
Copy link
Contributor Author

knz commented Jan 23, 2020

Why? What's wrong with writing after calling SetFixedTimestamp?

It's totally wrong enabling KV writes by SQL transactions in an AOST txn.

@andreimatei
Copy link
Contributor

andreimatei commented Jan 23, 2020

The way I see it, if it is wrong, then it should be enforced just like a set transaction_read_only=true is enforced - and I think that should be enforced by SQL. As far as KV is concerned, I don't think we should tie functions that offer some control over the timestamp (SetFixedTimestamp() \ CommitTimestampFixed()) with read-only semantics.

But I don't necessarily think that it's wrong for AOST transactions to write (although perhaps you should opt into it). If what you want is to write based on data from the past, I think there are use cases for that. The writes will fail, of course, if they're trying to overwrite more recent writes. But if you're writing to a unique row (say report_20200123), it should work. Of course, it'd be cool to also offer a way to read in the past and write in the present (i.e. specify AOST on a subquery).

@andreimatei
Copy link
Contributor

  • I should have said that the writes will also fail if what you're trying to write has been read more recently, or if a GC has run.

@tbg
Copy link
Member

tbg commented Jan 24, 2020

Even if we had an API for read-only txns in place, we would want awareness in SQL to avoid writing in such transactions (clearly broken right now). My personal preference would be to focus on fixing that. After that point, any kind of check we add to in *client.Txn is just an extra layer of safety, and I'd rather place that assertion in SQL to not jumble *client.Txn up more. That said, if we did revisit the client.Txn API, I could see us wanting to add true read-only transactions, and we'd certainly want to instantiate one for AOST. Andrei is right that there's nothing wrong technically with writing after fixing a timestamp, but I do think we'd want to stay away from it in practice - I don't see that this would have a place in a clean API.

@knz
Copy link
Contributor Author

knz commented Jan 24, 2020

I think I now want both levels of checks. I'll look into it.

@andreimatei
Copy link
Contributor

Andrei is right that there's nothing wrong technically with writing after fixing a timestamp, but I do think we'd want to stay away from it in practice - I don't see that this would have a place in a clean API.

The clean API would be one that separates the concerns - fixing of the timestamp vs protection against programming errors by refusing some operations. It's one thing to not particularly try to support features around "writing in the past", but it's another thing to disallow them by fiat.

My worry with starting to teach KV about read-only txns is that there's a logical level and there's a physical level and both could in theory be desired, and I'm afraid of trying to decide which is which. Should a read-only txn push other txns? Should a read-only transaction clean up intents from other transactions? Should it cause range leases to be acquired? Should it causes liveness records to have their epochs bumped?

@knz
Copy link
Contributor Author

knz commented Jan 29, 2020

It's one thing to not particularly try to support features around "writing in the past", but it's another thing to disallow them by fiat.

I'll grant you that.

Should a read-only txn push other txns? Should a read-only transaction clean up intents from other transactions? Should it cause range leases to be acquired? Should it causes liveness records to have their epochs bumped?

I think all these are valid questions but somewhat orthogonal. Once we have introduced the notion of read-only txns, we can decide (later) whether/how we can optimize them further.

@andreimatei
Copy link
Contributor

A side note on the transaction_read_only session variable:
The transaction_read_only variable is weird. It doesn't seem to be documented much in PG, as far as I can find. But I've played around, and it seems to affect only the current transaction; it gets reset afterwards. If you run it outside of a txn, it basically has no effect. Postgres doesn't let you set a value for this on the connection string.
In Postgres, this variable seems to have a role for read-only replicas.

For us a value coming from the connection string, through Defaults["transaction_read_only"] does not dictate the read-only setting of new transactions on that connection. It does, however, control RESET transaction_read_only, which is probably a bad thing, but also probably not worth the specific code to prevent it (I guess we'd want a mechanism to say that transaction_read_only is configurable, but not accepting values through the connection string.

@jlinder jlinder added the T-kv KV Team label Jun 16, 2021
@github-actions
Copy link

We have marked this issue as stale because it has been inactive for
18 months. If this issue is still relevant, removing the stale label
or adding a comment will keep it active. Otherwise, we'll close it in
10 days to keep the issue queue tidy. Thank you for your contribution
to CockroachDB!

@knz knz added T-sql-foundations SQL Foundations Team (formerly SQL Schema + SQL Sessions) and removed T-kv KV Team no-issue-activity labels Sep 19, 2023
@rafiss rafiss added db-cy-23 and removed db-cy-23 labels Sep 19, 2023
@michae2 michae2 self-assigned this Mar 7, 2024
craig bot pushed a commit that referenced this issue Mar 8, 2024
120097: sql: mark implicit transactions with AOST as read-only r=yuzefovich,rafiss a=michae2

**sql: mark implicit transactions with AOST as read-only**

In `handleAOST` we were setting the transaction AOST but not marking it as read-only. It needs to also be marked as read-only to disallow mutation statements and locking statements.

Fixes: #120081

Release note (sql change): Mutation statements such as UPDATE and DELETE as well as locking statements such as SELECT FOR UPDATE are not allowed in read-only transactions or AS OF SYSTEM TIME transactions. Fix an oversight where we were allowing mutation statements and locking statements in implicit single-statement transactions using AS OF SYSTEM TIME.

---

**sql: disallow SET transaction_read_only = false during AOST txn**

Fixes: #44200

Release note (bug fix): Fix a bug in which it was possible to `SET transaction_read_only = false` during an AS OF SYSTEM TIME transaction.

120147: catalog: add maybeAddConstraintIDs to RestoreChanges r=rafiss a=rafiss

This partially reverts 8b56efd

All backups taken in versions 22.1 and later should already have constraint IDs set. Technically, now that we are in v24.1, we no longer support restoring from versions older than 22.1, but we still have tests that restore from old versions. Until those fixtures are updated, we need to handle the case where constraint IDs are not set already when restoring a backup.

To prove that this works, I've resurrected a different test that restores from a v21.1 backup, which was earlier deleted in 8b56efd.

informs #120146
Release note: None

120148: tests: move some tests to `heavy` pools in `race`, `deadlock` r=rail a=rickystewart

Epic: CRDB-8308
Release note: None

Co-authored-by: Michael Erickson <[email protected]>
Co-authored-by: Rafi Shamim <[email protected]>
Co-authored-by: Ricky Stewart <[email protected]>
@craig craig bot closed this as completed in 238ca33 Mar 8, 2024
michae2 added a commit to michae2/cockroach that referenced this issue Mar 8, 2024
Fixes: cockroachdb#44200

Release note (bug fix): Fix a bug in which it was possible to `SET
transaction_read_only = false` during an AS OF SYSTEM TIME transaction.
michae2 added a commit to michae2/cockroach that referenced this issue Mar 13, 2024
Fixes: cockroachdb#44200

Release note (bug fix): Fix a bug in which it was possible to `SET
transaction_read_only = false` during an AS OF SYSTEM TIME transaction.
@github-project-automation github-project-automation bot moved this to Closed in KV Aug 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-sql-optimizer SQL logical planning and optimizations. C-bug Code not up to spec/doc, specs & docs deemed correct. Solution expected to change code/behavior. S-3-erroneous-edge-case Database produces or stores erroneous data without visible error/warning, in rare edge cases. T-sql-foundations SQL Foundations Team (formerly SQL Schema + SQL Sessions)
Projects
No open projects
Status: Closed
Status: Done
Development

Successfully merging a pull request may close this issue.

7 participants