postgresql/src/test/isolation
Tom Lane 70b42f2790 Fix misbehavior of EvalPlanQual checks with multiple result relations.
The idea of EvalPlanQual is that we replace the query's scan of the
result relation with a single injected tuple, and see if we get a
tuple out, thereby implying that the injected tuple still passes the
query quals.  (In join cases, other relations in the query are still
scanned normally.)  This logic was not updated when commit 86dc90056
made it possible for a single DML query plan to have multiple result
relations, when the query target relation has inheritance or partition
children.  We replaced the output for the current result relation
successfully, but other result relations were still scanned normally;
thus, if any other result relation contained a tuple satisfying the
quals, we'd think the EPQ check passed, even if it did not pass for
the injected tuple itself.  This would lead to update or delete
actions getting performed when they should have been skipped due to
a conflicting concurrent update in READ COMMITTED isolation mode.

Fix by blocking all sibling result relations from emitting tuples
during an EvalPlanQual recheck.  In the back branches, the fix is
complicated a bit by the need to not change the size of struct
EPQState (else we'd have ABI-breaking changes in offsets in
struct ModifyTableState).  Like the back-patches of 3f7836ff6
and 4b3e37993, add a separately palloc'd struct to avoid that.
The logic is the same as in HEAD otherwise.

This is only a live bug back to v14 where 86dc90056 came in.
However, I chose to back-patch the test cases further, on the
grounds that this whole area is none too well tested.  I skipped
doing so in v11 though because none of the test applied cleanly,
and it didn't quite seem worth extra work for a branch with only
six months to live.

Per report from Ante Krešić (via Aleksander Alekseev)

Discussion: https://postgr.es/m/CAJ7c6TMBTN3rcz4=AjYhLPD_w3FFT0Wq_C15jxCDn8U4tZnH1g@mail.gmail.com
2023-05-19 14:26:40 -04:00
..
expected Fix misbehavior of EvalPlanQual checks with multiple result relations. 2023-05-19 14:26:40 -04:00
specs Fix misbehavior of EvalPlanQual checks with multiple result relations. 2023-05-19 14:26:40 -04:00
.gitignore Build all Flex files standalone 2022-09-04 12:09:01 +07:00
Makefile Build all Flex files standalone 2022-09-04 12:09:01 +07:00
README Replace PGISOLATIONTIMEOUT with 2 * PG_TEST_TIMEOUT_DEFAULT. 2022-07-01 18:27:50 -07:00
isolation_main.c Update copyright for 2023 2023-01-02 15:00:37 -05:00
isolation_schedule Fix assert failures in parallel SERIALIZABLE READ ONLY. 2023-03-06 15:07:15 +13:00
isolationtester.c Remove configure probe for sys/select.h. 2022-08-14 00:09:47 +12:00
isolationtester.h Update copyright for 2023 2023-01-02 15:00:37 -05:00
meson.build Update copyright for 2023 2023-01-02 15:00:37 -05:00
specparse.y Update copyright for 2023 2023-01-02 15:00:37 -05:00
specscanner.l Update copyright for 2023 2023-01-02 15:00:37 -05:00

README

src/test/isolation/README

Isolation tests
===============

This directory contains a set of tests for concurrent behaviors in
PostgreSQL.  These tests require running multiple interacting transactions,
which requires management of multiple concurrent connections, and therefore
can't be tested using the normal pg_regress program.  The name "isolation"
comes from the fact that the original motivation was to test the
serializable isolation level; but tests for other sorts of concurrent
behaviors have been added as well.

You can run the tests against the current build tree by typing
    make check
Alternatively, you can run against an existing installation by typing
    make installcheck
(This will contact a server at the default port expected by libpq.
You can set PGPORT and so forth in your environment to control this.)

To run just specific test(s) against an installed server,
you can do something like
    ./pg_isolation_regress fk-contention fk-deadlock
(look into the specs/ subdirectory to see the available tests).

Certain tests require the server's max_prepared_transactions parameter to be
set to at least 3; therefore they are not run by default.  To include them in
the test run, use
    make check-prepared-txns
or
    make installcheck-prepared-txns
after making sure the server configuration is correct (see TEMP_CONFIG
to adjust this in the "check" case).

To define tests with overlapping transactions, we use test specification
files with a custom syntax, which is described in the next section.  To add
a new test, place a spec file in the specs/ subdirectory, add the expected
output in the expected/ subdirectory, and add the test's name to the
isolation_schedule file.

isolationtester is a program that uses libpq to open multiple connections,
and executes a test specified by a spec file. A libpq connection string
specifies the server and database to connect to; defaults derived from
environment variables are used otherwise.

pg_isolation_regress is a tool similar to pg_regress, but instead of using
psql to execute a test, it uses isolationtester.  It accepts all the same
command-line arguments as pg_regress.

By default, isolationtester will wait at most 360 seconds (6 minutes)
for any one test step to complete.  If you need to adjust this, set
the environment variable PG_TEST_TIMEOUT_DEFAULT to half the desired
timeout in seconds.


Test specification
==================

Each isolation test is defined by a specification file, stored in the specs
subdirectory.  A test specification defines some SQL "steps", groups them
into "sessions" (where all the steps of one session will be run in the
same backend), and specifies the "permutations" or orderings of the steps
that are to be run.

A test specification consists of four parts, in this order:

setup { <SQL> }

  The given SQL block is executed once (per permutation) before running
  the test.  Create any test tables or other required objects here.  This
  part is optional.  Multiple setup blocks are allowed if needed; each is
  run separately, in the given order.  (The reason for allowing multiple
  setup blocks is that each block is run as a single PQexec submission,
  and some statements such as VACUUM cannot be combined with others in such
  a block.)

teardown { <SQL> }

  The teardown SQL block is executed once after the test is finished. Use
  this to clean up in preparation for the next permutation, e.g dropping
  any test tables created by setup. This part is optional.

session <name>

  There are normally several "session" parts in a spec file. Each
  session is executed in its own connection. A session part consists
  of three parts: setup, teardown and one or more "steps". The per-session
  setup and teardown parts have the same syntax as the per-test setup and
  teardown described above, but they are executed in each session. The setup
  part might, for example, contain a "BEGIN" command to begin a transaction.

  Each step has the syntax

  step <name> { <SQL> }

  where <name> is a name identifying this step, and <SQL> is a SQL statement
  (or statements, separated by semicolons) that is executed in the step.
  Step names must be unique across the whole spec file.

permutation <step name> ...

  A permutation line specifies a list of steps that are run in that order.
  Any number of permutation lines can appear.  If no permutation lines are
  given, the test program automatically runs all possible interleavings
  of the steps from each session (running the steps of any one session in
  order).  Note that the list of steps in a manually specified
  "permutation" line doesn't actually have to be a permutation of the
  available steps; it could for instance repeat some steps more than once,
  or leave others out.  Also, each step name can be annotated with some
  parenthesized markers, which are described below.

Session and step names are SQL identifiers, either plain or double-quoted.
A difference from standard SQL is that no case-folding occurs, so that
FOO and "FOO" are the same name while FOO and Foo are different,
whether you quote them or not.  You must use quotes if you want to use
an isolation test keyword (such as "permutation") as a name.

A # character begins a comment, which extends to the end of the line.
(This does not work inside <SQL> blocks, however.  Use the usual SQL
comment conventions there.)

There is no way to include a "}" character in an <SQL> block.

For each permutation of the session steps (whether these are manually
specified in the spec file, or automatically generated), the isolation
tester runs the main setup part, then per-session setup parts, then
the selected session steps, then per-session teardown, then the main
teardown script.  Each selected step is sent to the connection associated
with its session.  The main setup and teardown scripts are run in a
separate "control" session.


Support for blocking commands
=============================

Each step may contain commands that block until further action has been taken
(most likely, some other session runs a step that unblocks it or causes a
deadlock).  A test that uses this ability must manually specify valid
permutations, i.e. those that would not expect a blocked session to execute a
command.  If a test fails to follow that rule, isolationtester will cancel it
after 2 * PG_TEST_TIMEOUT_DEFAULT seconds.  If the cancel doesn't work,
isolationtester will exit uncleanly after a total of 4 *
PG_TEST_TIMEOUT_DEFAULT.  Testing invalid permutations should be avoided
because they can make the isolation tests take a very long time to run, and
they serve no useful testing purpose.

Note that isolationtester recognizes that a command has blocked by looking
to see if it is shown as waiting in the pg_locks view; therefore, only
blocks on heavyweight locks will be detected.


Dealing with race conditions
============================

In some cases, the isolationtester's output for a test script may vary
due to timing issues.  One way to deal with that is to create variant
expected-files, which follow the usual PG convention that variants for
foo.spec are named foo_1.out, foo_2.out, etc.  However, this method is
discouraged since the extra files are a nuisance for maintenance.
Instead, it's usually possible to stabilize the test output by applying
special markers to some of the step names listed in a permutation line.

The general form of a permutation entry is

	<step name> [ ( <marker> [ , <marker> ... ] ) ]

where each marker defines a "blocking condition".  The step will not be
reported as completed before all the blocking conditions are satisfied.
The possible markers are:

	*
	<other step name>
	<other step name> notices <n>

An asterisk marker, such as mystep(*), forces the isolationtester to
report the step as "waiting" as soon as it's been launched, regardless of
whether it would have been detected as waiting later.  This is useful for
stabilizing cases that are sometimes reported as waiting and other times
reported as immediately completing, depending on the relative speeds of
the step and the isolationtester's status-monitoring queries.

A marker consisting solely of a step name indicates that this step may
not be reported as completing until that other step has completed.
This allows stabilizing cases where two queries might be seen to complete
in either order.  Note that this step can be *launched* before the other
step has completed.  (If the other step is used more than once in the
current permutation, this step cannot complete while any of those
instances is active.)

A marker of the form "<other step name> notices <n>" (where <n> is a
positive integer) indicates that this step may not be reported as
completing until the other step's session has returned at least <n>
NOTICE messages, counting from when this step is launched.  This is useful
for stabilizing cases where a step can return NOTICE messages before it
actually completes, and those messages must be synchronized with the
completions of other steps.

Notice that these markers can only delay reporting of the completion
of a step, not the launch of a step.  The isolationtester will launch
the next step in a permutation as soon as (A) all prior steps of the
same session are done, and (B) the immediately preceding step in the
permutation is done or deemed blocked.  For this purpose, "deemed
blocked" means that it has been seen to be waiting on a database lock,
or that it is complete but the report of its completion is delayed by
one of these markers.

In some cases it is important not to launch a step until after the
completion of a step in another session that could have been deemed
blocked.  An example is that if step s1 in session A is issuing a
cancel for step s2 in session B, we'd better not launch B's next step
till we're sure s1 is done.  If s1 is blockable, trouble could ensue.
The best way to prevent that is to create an empty step in session A
and run it, without any markers, just before the next session B step.
The empty step cannot be launched until s1 is done, and in turn the
next session B step cannot be launched until the empty step finishes.