github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/docs/RFCS/20171024_select_for_update.md (about)

     1  - Feature Name: Support `SELECT FOR UPDATE`
     2  - Status: postponed
     3  - Start Date: 2017-10-17
     4  - Authors: Rebecca Taft
     5  - RFC PR: [#19577](https://github.com/cockroachdb/cockroach/pull/19577)
     6  - Cockroach Issue: [#6583](https://github.com/cockroachdb/cockroach/issues/6583)
     7  
     8  # Summary
     9  
    10  This RFC is postponed because it seems that, given CockroachDB's model of concurrency control, 
    11  it is not possible to implement the functionality that users would expect for `SELECT ... FOR UPDATE`.
    12  None of the implementation alternatives we have examined would fully replicate the semantics that Postgres
    13  provides, and there is a risk that customers would try to use the feature without fully
    14  understanding the pitfalls. The details are described below in the [Drawbacks](#drawbacks) and
    15  [Alternatives](#alternatives) sections. We may revisit this feature if there is sufficient demand from
    16  customers, or if we can prove that there is a significant benefit to using `SELECT ... FOR UPDATE`
    17  for certain applications.
    18  
    19  Original Summary: Support the `SELECT ... FOR UPDATE` SQL syntax, which locks rows returned by the `SELECT`
    20  statement. This pessimistic locking feature prevents concurrent transactions from updating
    21  any of the locked rows until the locking transaction commits or aborts. This is useful for 
    22  enforcing consistency when running in `SNAPSHOT` mode, and may be useful for avoiding deadlocks
    23  when running in `SERIALIZABLE` mode. Several potential customers have asked for this feature,
    24  and it would also get us closer to feature parity with Postgres. The proposed implementation is
    25  to set row-level "dummy" intents by transforming the `SELECT ... FOR UPDATE` query tree to
    26  include an `updateNode`.
    27  
    28  # Motivation
    29  
    30  As described in [Issue #6583](https://github.com/cockroachdb/cockroach/issues/6583),
    31  `SELECT ... FOR UPDATE` is not standard SQL, but many databases now support it, including
    32  Postgres. Thus the primary motivation for this feature is compatibility with existing code. 
    33  Several third party products such as the [Quartz Scheduler](http://www.quartz-scheduler.org),
    34  [OpenJPA](http://openjpa.apache.org) and [Liquibase](http://www.liquibase.org)
    35  also rely on this feature, preventing some potential customers from switching to CockroachDB.
    36  
    37  In some cases, `SELECT ... FOR UPDATE` is required to maintain correctness when running CockroachDB
    38  in `SNAPSHOT` mode. In particular, `SELECT ... FOR UPDATE` can be used to prevent write skew anomalies.
    39  Write skew anomalies occur when two concurrent transactions read an overlapping set of
    40  rows but update disjoint sets of rows. Since the transactions each operate on private snapshots of
    41  the database, neither one will see the updates from the other.
    42  
    43  The [Wikipedia entry on Snapshot Isolation](https://en.wikipedia.org/wiki/Snapshot_isolation) has a
    44  useful concrete example:
    45  
    46  > ... imagine V1 and V2 are two balances held by a single person, Phil. The bank will allow either
    47  V1 or V2 to run a deficit, provided the total held in both is never negative (i.e. V1 + V2 ≥ 0).
    48  Both balances are currently $100. Phil initiates two transactions concurrently, T1 withdrawing $200
    49  from V1, and T2 withdrawing $200 from V2. .... T1 and T2 operate on private snapshots of the database:
    50  each deducts $200 from an account, and then verifies that the new total is zero, using the other account
    51  value that held when the snapshot was taken. Since neither update conflicts, both commit successfully,
    52  leaving V1 = V2 = -$100, and V1 + V2 = -$200.
    53  
    54  It is possible to prevent this scenario from happening in `SNAPSHOT` mode by using `SELECT ... FOR UPDATE`.
    55  For example, if each transaction calls something like:
    56  `SELECT * FROM accounts WHERE acct_name = 'V1' OR acct_name = 'V2' FOR UPDATE` at the start of the transaction,
    57  one of the transactions would be blocked until the "winning" transaction commits and releases the locks.
    58  At that point, the "losing" transaction would be able to see the update from the winner, so it would not deduct
    59  $200. Therefore, using `SELECT ... FOR UPDATE` lets users obtain some of the performance benefits of using `SNAPSHOT`
    60  isolation instead of `SERIALIZABLE`, without paying the price of write skew anomalies.
    61  
    62  `SELECT ... FOR UPDATE` is not needed for correctness when running in `SERIALIZABLE` mode, 
    63  but it may still be useful for controlling lock ordering and avoiding deadlocks. For example,
    64  consider the following schedule:
    65  
    66  ```
    67  T1: Starts transaction
    68  T2: Starts transaction
    69  T1: Updates row A
    70  T2: Updates row B
    71  T1: Wants to update row B (blocks)
    72  T2: Wants to update row A (deadlock)
    73  ```
    74  
    75  This sort of scenario can happen in any database that tries to maintain some level of correctness. 
    76  It is especially common in databases that use pessimistic two-phased locking (2PL) since transactions
    77  must acquire shared locks for reads in addition to exclusive locks for writes. But deadlocks like the
    78  one shown above also happen in databases that use MVCC like PostgreSQL and CockroachDB, since writes must
    79  acquire locks on all rows that will be updated. Postgres, CockroachDB, and many other systems detect deadlocks by
    80  identifying cycles in a "waits-for" graph, where nodes represent transactions, and directed edges represent
    81  transactions waiting on each other to release locks (or write intents). In Postgres, if a cycle (deadlock) is detected,
    82  transactions will be selectively aborted until the cycle(s) are removed. CockroachDB forces one of the transactions
    83  in the cycle to be "pushed", which generally has the effect of aborting at least one transaction. CockroachDB
    84  can perform this detection and push almost instantaneously after the conflict happens, so "deadlocks" in CockroachDB
    85  are less disruptive than in Postgres, where deadlock detection can take up to a second. Some other systems
    86  use a timeout mechanism, where transactions will abort after waiting a certain amount of time to acquire a lock.
    87  In all cases, the deadlock causes delays and/or aborted transactions.
    88  
    89  `SELECT ... FOR UPDATE` will help avoid deadlocks by allowing transactions to acquire all of their locks
    90  (lay down intents in CockroachDB) up front. For example, the above schedule would change to the following:
    91  
    92  ```
    93  T1: Starts transaction
    94  T2: Starts transaction
    95  T1: Locks rows A and B
    96  T1: Updates row A
    97  T2: Wants to lock rows A and B (blocks)
    98  T1: Updates row B
    99  T1: Commits
   100  T2: Locks rows A and B
   101  T2: Updates row B
   102  T2: Updates row A
   103  T2: Commits
   104  ```
   105  
   106  Since both transactions attempted to lock rows A and B at the start of the transaction, the deadlock was prevented. 
   107  Acquiring all locks (or laying down write intents) up front allows the database to lock rows in a consistent order
   108  (even if they are updated in a different order), thus preventing deadlocks. 
   109  
   110  Many implementations of this feature also include options to control whether or not to wait on locks.
   111  `SELECT ... FOR UPDATE NOWAIT` is one option, which causes the query to return an error if it is
   112  unable to immediately lock all target rows. This is useful for latency-critical situations,
   113  and could also be useful for auto-retrying transactions in CockroachDB. `SELECT ... FOR UPDATE SKIP LOCKED`
   114  is another option, which returns only the rows that could be locked immediately, and skips over the others.
   115  This option returns an inconsistent view of the data, but may be useful for cases when multiple
   116  workers are trying to process data in the same table as if it were a queue of tasks. 
   117  The default behavior of `SELECT ... FOR UPDATE` is for the transaction to block if some of the
   118  target rows are already locked by another transaction. Note that it is not possible to use the
   119  `NOWAIT` and `SKIP LOCKED` modifiers without `FOR { UPDATE | SHARE | ... }`.
   120  
   121  The first implementation of `FOR UPDATE` in CockroachDB will not include `NOWAIT` or `SKIP LOCKED` options.
   122  It seems that some users want these features, but many would be satisfied with `FOR UPDATE` alone.
   123  As of this writing we are not aware of any commonly used third-party products that use these options.
   124  
   125  # Guide-level explanation
   126  
   127  The [Postgres Documentation](https://www.postgresql.org/docs/current/static/sql-select.html#sql-for-update-share)
   128  describes this feature as it is supported by Postgres. As shown, the syntax of the locking clause has the form
   129  
   130  ```
   131  FOR lock_strength [ OF table_name [, ...] ] [ NOWAIT | SKIP LOCKED ]
   132  ```
   133  
   134  where `lock_strength` can be one of
   135  
   136  ```
   137  UPDATE
   138  NO KEY UPDATE
   139  SHARE
   140  KEY SHARE
   141  ```
   142  
   143  For our initial implementation in CockroachDB, we will likely simplify this syntax to
   144  
   145  ```
   146  FOR UPDATE
   147  ```
   148  
   149  i.e., no variation in locking strength, no specified tables, and no options for avoiding
   150  waiting on intents. Using `FOR UPDATE` will result in laying intents on the rows returned by the `SELECT`
   151  query. Note that it only lays intents for rows that already exist; preventing inserts matching
   152  the `SELECT` query is not desired. As described above, this feature alone is useful because it helps
   153  maintain correctness when running CockroachDB in `SNAPSHOT` mode (avoiding write skew), and serves
   154  as a tool for optimization (avoiding deadlocks) when running in `SERIALIZABLE` mode.
   155  
   156  For example, consider the following transaction:
   157  
   158  <a name="employees_transaction"></a>
   159  ```
   160  BEGIN;
   161  
   162  SELECT * FROM employees WHERE name = 'John Smith' FOR UPDATE;
   163  
   164  ...
   165  
   166  UPDATE employees SET salary = 50000 WHERE name = 'John Smith';
   167  
   168  COMMIT;
   169  ```
   170  
   171  This code will lay intents on the rows of all employees named John Smith at the beginning of the transaction,
   172  preventing other concurrent transactions from simultaneously updating those rows.
   173  As a result, the `UPDATE employees ...` statement at the end of the transaction will not need
   174  to lay any additional intents. Note that `FOR UPDATE` will have no effect if it is used in a
   175  stand-alone query that is not part of any transaction.
   176  
   177  One important difference between CockroachDB and Postgres relates to transaction priorities. In CockroachDB,
   178  if there is a conflict and the second transaction has a higher priority, the first transaction will be
   179  pushed out of the way -- even if it has laid down intents already. This would apply to transactions with
   180  `SELECT ... FOR UPDATE`, just as it would for any other transaction.
   181  
   182  # Reference-level explanation
   183  
   184  This section provides more detail about how and why the CockroachDB implementation of
   185  the locking clause will differ from Postgres.
   186  
   187  With the current model of CockroachDB, it is not possible to support the locking strengths
   188  `NO KEY UPDATE` or `KEY SHARE` because
   189  these options require locking at a sub-row granularity. It is also not clear that CockroachDB can support
   190  `SHARE`, because there is currently no such thing as a "read intent". `UPDATE` can be supported by
   191  marking the affected rows with dummy write intents.
   192  
   193  By default, if `FOR UPDATE` is used in Postgres without specifying tables
   194  (without the `OF table_name [, ...]` clause),
   195  Postgres will lock all rows returned by the `SELECT` query. The `OF table_name [, ...]` clause
   196  enables locking only the rows in the specified tables. To lock different tables with different
   197  strengths or different options, Postgres users can string multiple locking clauses together. 
   198  For example,
   199  
   200  ```
   201  SELECT * from employees e, departments d, companies c
   202  WHERE e.did = d.id AND d.cid = c.id
   203  AND c.name = `Cockroach Labs`
   204  FOR UPDATE OF employees SKIP LOCKED
   205  FOR SHARE OF departments NOWAIT
   206  ```
   207  locks rows in the `employees` table that satisfy the join condition with an exclusive lock,
   208  and skips over rows that are already locked by another transaction.
   209  It also locks rows in the `departments` table that satisfy the join condition with a shared lock,
   210  and returns an error if it cannot lock all of the rows immediately. It does not lock
   211  the `companies` table.
   212  
   213  Implementing this flexibility in CockroachDB for use of different tables and different options
   214  may be excessively complicated, and it's not clear that our customers actually need
   215  it. To avoid spending too much time on this, as mentioned above, we will probably just implement the most
   216  basic functionality in which clients use `FOR UPDATE` to lay intents on the rows returned by the query.
   217  Initially we won't include the `SKIP LOCKED` or `NOWAIT` options, but it may be worth implementing
   218  these at some point.
   219  
   220  At the moment `FOR UPDATE` is disabled for use in views (there will not be an error, but it will
   221  be ignored). This is similar to the way `ORDER BY` and `LIMIT` are handled in views. See comment from @a-robinson in
   222  [data_source.go:getViewPlan()](https://github.com/cockroachdb/cockroach/blob/5a6b4312a972b74b0af5de53dfdfb204dc0fd6d7/pkg/sql/data_source.go#L680).
   223  As described in the comment, the outer `Select` AST node is currently being stripped out of the view plan.
   224  If `ORDER BY` and `LIMIT` are enabled later by including the entire `Select`, `FOR UPDATE` would come for free.
   225  Postgres supports all of these options in views, since it supports any `SELECT` query, and re-runs
   226  the query each time the view is used. CockroachDB should do the same.
   227  
   228  With the (temporary) exception of views, `FOR UPDATE` should propagate throughout the query plan
   229  as expected. If `FOR UPDATE` only occurs in a subquery, the rows locked are those returned by the subquery
   230  to the outer query (unless the optimizer reduces the amount of data scanned in the subquery). For example,
   231  `SELECT * FROM employees e, (SELECT * FROM departments FOR UPDATE) d WHERE e.did = d.id`
   232  would only lock rows in the departments table. If the `FOR UPDATE` occurs on the outer `SELECT`, however,
   233  all rows returned by the query will be locked.
   234  
   235  If the number of intents exceeds `maxIntents` as defined in `txn_coord_sender.go`
   236  (default 100,000), the transaction will be rejected. This is similar to the way updates work,
   237  and will prevent users from using `SELECT FOR UPDATE` with queries that would result in a full table
   238  scan. If we need to reduce this number later we can add a new setting just for `SELECT FOR UPDATE`.
   239  
   240  One issue that is not discussed in detail in the Postgres documentation is the order in which
   241  locks are acquired. Acquiring locks in a consistent order is an important tool to prevent deadlocks
   242  from occuring. For the CockroachDB implementation of `FOR UPDATE`, we will not implement the feature
   243  in DistSQL at this time (DistSQL does not currently support writes), so our implementation
   244  will most likely produce a consistent ordering of write intents (we currently have no evidence
   245  to the contrary). It is difficult to guarantee a particular ordering, however, since the implementation
   246  of the local execution engine may change in the future to take advantage of parallel processing. Likewise,
   247  if `FOR UPDATE` is supported later in DistSQL, ordering will be more difficult to guarantee.
   248  
   249  ## Detailed design
   250  
   251  There are a number of changes that will need to be implemented in the SQL layer in order to
   252  support `FOR UPDATE`.
   253  
   254  - Update the parser to support the syntax in `SELECT` statements.
   255  - Add checks to ensure that `FOR UPDATE` cannot be used with `GROUP BY`, `HAVING`, `WINDOW`, `DISTINCT`,
   256    `UNION`, `INTERSECT`, `EXCEPT`, or in contexts where returned rows cannot be clearly identified with
   257    individual table rows; for example it cannot be used with aggregation. Postgres also disallows
   258    `FOR UPDATE` with all of these query types.
   259  - Possibly add additional checks for the first implementation to limit `SELECT FOR UPDATE` queries to
   260    "simple" queries where the primary key must be used in the `WHERE` clause, and the predicate must be either `==` or `IN`.
   261  - Modify the query tree so that the top level `renderNode` is transformed to an `updateNode` which sets each selected
   262    column value to itself and returns the selected rows (e.g., `UPDATE table SET a = a RETURNING a`). If needed,
   263    a `sortNode` and/or `limitNode` can be applied above the `updateNode` in the query tree.
   264  
   265  Note that none of this design involves DistSQL. At the moment DistSQL does not support writes,
   266  so it would not make sense to support `FOR UPDATE`.
   267  
   268  ## <a name="drawbacks"></a>Drawbacks
   269  
   270  We have decided to postpone this feature because it is not clear that the proposed solution
   271  (or any of the alternatives discussed below) would meet customer needs.
   272  
   273  Some potential customers (such as those using the Quartz Scheduler) use `SELECT FOR UPDATE`
   274  as if it were an advisory lock; they never actually update the rows selected by the `SELECT FOR UPDATE`
   275  statement, so the only purpose of the lock is to prevent other concurrent transactions from accessing some 
   276  shared resource during the transaction. Although the proposed solution of laying intents
   277  will achieve this isolation, it will result in many aborted transactions. For example, consider a
   278  transaction `T1` with a lower timestamp than another transaction `T2`. If `T1` tries to access rows
   279  already marked with an intent by `T2`, `T1` will block until `T2` commits or aborts, and then `T1` will
   280  abort with a retryable error. Most existing codebases using `SELECT FOR UPDATE` are probably not equipped
   281  to handle these retryable errors because other databases would not cause transactions to abort in this scenario.
   282  
   283  The key motivation described above for using `SELECT FOR UPDATE` in `SERIALIZABLE` mode is to control
   284  lock ordering so as to minimize transaction aborts. Due to the way concurrency control works in
   285  CockroachDB, however, `SELECT FOR UPDATE` is still likely to cause many aborts (as described in the previous
   286  paragraph). Postgres also recommends against using this feature in `SERIALIZABLE` mode, since it is not necessary
   287  for correctness, and can degrade performance by causing disk accesses to set locks. Additionally, although `FOR UPDATE`
   288  can prevent deadlocks if used judiciously, it can also cause deadlocks if not every transaction
   289  sets intents in the same order. Since `FOR UPDATE` results in transactions setting more intents
   290  for a longer period of time, the chance of collision is higher. There may be some benefit to using `FOR UPDATE`
   291  with high-contention workloads since it would cause conflicting transactions to fail earlier and do less work
   292  before aborting, but we would need to run some tests to validate this (perhaps by running benchmarks
   293  with high-contention workloads). If we do eventually implement this feature, we should probably discourage customers
   294  from using `FOR UPDATE` in `SERIALIZABLE` mode unless they have good reasons to use it.
   295  
   296  It's also not clear that this feature is worth implementing for use in `SNAPSHOT` mode.
   297  As mentioned above, the primary motivation for this feature is compatibility with existing code.
   298  Simply running in `SERIALIZABLE` mode is (probably) a better way of avoiding write skew than `SNAPSHOT`
   299  +`FOR UPDATE`.
   300  
   301  Given the above drawbacks, we have decided it is not worth the effort to implement `SELECT FOR UPDATE` at
   302  this time. But if we do implement it in the future, one way to make sure customers avoid the pitfalls
   303  is to create an "opt-in" setting for using this feature. By default, using `FOR UPDATE` would throw an
   304  error and direct users to view the documentation. Only users who explicitly opt in by updating their cluster
   305  or session settings would be able to use the feature. We could also add an option to either use the feature
   306  with intents or as a no-op (see the [Isolation Upgrade](#no-op) alternative below). Adding options adds
   307  complexity, however.
   308  
   309  If we implement this feature, we should probably start with only `FOR UPDATE`, and not
   310  include other features (e.g., `NOWAIT`, `SKIP LOCKED`, etc). It will be a lot of work to implement
   311  all of the features supported by Postgres, and it is probably not worth our time since it's not clear
   312  these features will actually get used. We can easily add them later if needed.
   313  
   314  In the mean time, customers who really need explicit locking functionality can emulate the
   315  feature by executing something like `UPDATE x SET y=y WHERE y=y`. Executing this command at
   316  the start of a transaction would effectively set intents on all of the rows in table `x`.
   317  
   318  ## <a name="alternatives"></a>Rationale and Alternatives
   319  
   320  The proposed solution is to "lock" rows by writing dummy write intents on each row as part
   321  of an update operation. However, there are a couple of alternative implementations worth
   322  considering, specifically row-level intents set as part of a scan operation,
   323  range-level intents, and isolation ugrade.
   324  
   325  ### Row-level intents set during scan
   326  
   327  Laying intents as part of an `UPDATE` operation could be expensive for simple `SELECT FOR UPDATE`
   328  queries since it requires multiple requests to the KV layer. The first KV request performs the scan
   329  operation, and subsequent requests update each row to lay an intent. For simple queries, a less
   330  expensive approach would be to lay intents directly during the scan operation.
   331  
   332  This approach has a few downsides, however. First, it would be more work to implement since 
   333  it would require updates to the KV API to include new messages (e.g., ScanForUpdate and ReverseScanForUpdate).
   334  These new messages would require updates to the KV and storage layers to mimic processing of Scan and ReverseScan
   335  and set dummy write intents on every row touched.
   336  
   337  As described in [issue #6583](https://github.com/cockroachdb/cockroach/issues/6583), 
   338  > Implementing this would touch a lot of different parts of the code. No part is individually too
   339  tricky, but there are a lot of them (`git grep -i reversescan` will give you an idea of the scope).
   340  The bulk of the changes would consist of implementing new ScanForUpdate and ReverseScanForUpdate
   341  calls in the KV API. These would work similarly to regular scans, but A) would be flagged as read/write
   342  commands instead of read-only and B) after performing the scan, they'd use MVCCPut to write back the
   343  values that were just read (that's not the most efficient way to do things, but I think it's the right
   344  way to start since it will have the right semantics without complicating the backwards-compatibility
   345  story). Then the SQL layer would use these instead of plan Scan/ReverseScan when `FOR UPDATE` has been
   346  requested.
   347  
   348  There was some discussion in the issue about whether we really needed new API calls, but
   349  the consensus was that making it possible to write on `Scan` requests would make debugging a nightmare.
   350  
   351  This approach would also require a change in the SQL layer to handle the case when a `SELECT FOR UPDATE`
   352  query would only scan a secondary index and not touch the primary key (PK) index. In this case,
   353  we would need to implicitly modify the query plan to add a join with the PK index so that intents
   354  would always be laid on the PK index. This would ensure that the `SELECT FOR UPDATE` query would prevent
   355  concurrent transactions from updating the corresponding rows, since updates must always lay
   356  intents on the PK index.
   357  
   358  Another downside is that in many cases this approach would set intents on more rows than returned by the
   359  `SELECT`, since most predicates are not applied until after the scan is completed. This should not affect
   360  correctness in terms of consistency or isolation, but could affect performance if there is high contention.
   361  For example, if the first `SELECT` statement in the [employees transaction shown above](#employees_transaction)
   362  were `SELECT * FROM employees WHERE name like '%Smith' FOR UPDATE;`, CockroachDB would set intents on
   363  all of the rows in the `employees` table because it's not possible to determine from the predicate
   364  which key spans are affected. This lack of precision would be an issue for any predicate that
   365  does not directly translate to particular key spans. Furthermore, since this translation may not be obvious
   366  to users, they could easily write queries that would result in full-table scans by accident.
   367  
   368  In contrast, Postgres (generally) locks exactly the rows returned by the query, and no more. There are a few
   369  examples given in the [documentation](https://www.postgresql.org/docs/current/static/sql-select.html#sql-for-update-share)
   370  where that's not the case. For example, `SELECT ... LIMIT 5 OFFSET 5 FOR UPDATE` may
   371  lock up to 10 rows even though only 5 rows are returned. 
   372  
   373  ### Range-level intents
   374  
   375  One alternative to row-level intents is to set an intent on an entire Range if the `SELECT` statement
   376  would return the majority of rows in the range. This is similar to the approach
   377  suggested by the [Revert Command RFC](https://github.com/cockroachdb/cockroach/pull/16294).
   378  
   379  The advantage of setting intents on entire ranges is that it would significantly improve the performance
   380  for large scans compared to setting intents on individual rows. The downside is that
   381  this feature is not yet implemented, so it would be significantly more effort than using 
   382  simple row-level intents. (It was actually deemed to be too complex and all-interfering in the
   383  [Revert Command RFC](https://github.com/cockroachdb/cockroach/pull/16294), which is why that
   384  RFC is now closed.) It's also not clear that customers would use `FOR UPDATE` with 
   385  large ranges, so this may be an unneeded performance optimization. Furthermore, 
   386  setting an intent on the entire range based on the predicate could result in "locking" rows that should
   387  not be observable by the `SELECT`. For instance, if the transaction performing the
   388  `SELECT FOR UPDATE` query over some range is at a lower timestamp than a later `INSERT`
   389  within that range, the `FOR UPDATE` should not apply to the newly written row.
   390  This issue is probably not any worse than the other problems with locking precision described above,
   391  though.
   392  
   393  One advantage of implementing range-level intents is that we could reuse this feature
   394  for other applications such as point-in-time recovery. The details of the proposed implementation
   395  as well as other possible applications are described in the [Revert Command RFC](https://github.com/cockroachdb/cockroach/pull/16294).
   396  However, in the interest of getting something working sooner rather than later,
   397  I believe row-level intents make more sense at this time.
   398  
   399  ### <a name="no-op"></a>Isolation upgrade
   400  
   401  Another alternative approach is to avoid setting any intents whatsoever. Instead, the `SELECT FOR UPDATE`
   402  would be a no-op for `SERIALIZABLE` transactions, and the database would automatically upgrade the isolation
   403  of a `SNAPSHOT` transaction to `SERIALIZABLE` when a `SELECT FOR UPDATE` is used. The advantage of this
   404  approach is its simplicity: the work required to support this feature would be minimal. Additionally,
   405  it would avoid the issues of lock precision and poor performance that exist in the other proposed solutions.
   406  Furthermore, it would successfully prevent write skew in
   407  `SNAPSHOT` transactions, which is a key reason many customers might want to use `SELECT FOR UPDATE`
   408  in the first place.
   409  
   410  The downside is that it may not be what our customers expect. It would not allow customers to control
   411  lock ordering, which as described above is one feature of `SELECT FOR UPDATE` that savvy users can employ to
   412  prevent deadlocks and other conflicts. Some users may also have reason to lock rows that they don't intend to
   413  update, and they will have no way to do that with this solution.
   414  
   415  ### Alternatives to FOR UPDATE
   416  
   417  So far this RFC has assumed that we want to implement some form of the `FOR UPDATE`
   418  SQL syntax. However, there are many other types of locks provided by Postgres, and
   419  it's possible that one of these other options would be a better choice for our customers.
   420  
   421  1. <b>Table Level Locks:</b>
   422     Postgres provides eight different types of table level locks: `ACCESS SHARE, ROW SHARE,`
   423     `ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE,` and `ACCESS EXCLUSIVE`.
   424     These locks can be acquired explicity using the `LOCK` command, but they are also implicitly
   425     acquired by different SQL statements. For example, a `SELECT` command (without `FOR UPDATE`)
   426     implicitly acquires the `ACCESS SHARE` lock on every table it accesses. This prevents concurrent
   427     calls to `DROP TABLE, TRUNCATE,` etc. on the same table, since those commands must acquire
   428     an `ACCESS EXCLUSIVE` lock, wchich conflicts with `ACCESS SHARE`. See the
   429     [PG docs](https://www.postgresql.org/docs/current/static/explicit-locking.html#locking-tables) 
   430     for the full list of conflicts.
   431  2. <b>Row Level Locks:</b>
   432     There are four different types of row level locks: `UPDATE, NO KEY UPDATE, SHARE, KEY SHARE`.
   433     As described above, these can be acquired explicitly with the `SELECT ... FOR locking_strength`
   434     command. This RFC has been focused on `SELECT ... FOR UPDATE`, but the other three options
   435     are available in Postgres to allow more concurrent accesses. The `UPDATE` (or under certain
   436     circumstances `NO KEY UPDATE`) locks are also acquired implicitly by `UPDATE` and `DELETE` commands.
   437     See the [PG docs](https://www.postgresql.org/docs/current/static/explicit-locking.html#locking-rows)
   438     for more details.
   439  3. <b>Advisory Locks:</b>
   440     Advisory locks are used to lock application-level resources, identified either by a single 64-bit key
   441     value or two 32-bit key values. It is up to the application programmer to ensure that locks are
   442     acquired and released at the correct points in time to ensure application-level resources are
   443     properly protected. Postgres provides numerous options for advisory locks. They can be:
   444     - Session-level (must be explicitly unlocked) or transaction-level (will be unlocked automatically at commit/abort)
   445     - Shared or exclusive
   446     - Blocking or non-blocking (e.g. `pg_try_advisory_lock` is non-blocking)
   447  
   448     All of the different advisory lock functions are listed in the [PG docs](https://www.postgresql.org/docs/current/static/functions-admin.html#functions-advisory-locks)
   449  
   450  There are valid arguments for using each of these different lock types in different applications.
   451  However, I do not think that either table-level locks or advisory locks will be a good
   452  substitue for our customers that require explicit row-level locks.
   453  Table-level locks are not a good substitue for `FOR UPDATE` and the other row-level locks
   454  because they are too coarse-grained and will cause unnecessary performance degradation.
   455  Advisory locks place too much responsibility on the application developer(s) to ensure that the
   456  appropriate lock is always acquired before accessing a given row. This doesn't mean we
   457  shouldn't support advisory locks, but I don't think we should force developers to use them
   458  in place of explicit row-level locks.