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

[YSQL] Batch FK trigger checks #2951

Closed
ndeodhar opened this issue Nov 16, 2019 · 1 comment
Closed

[YSQL] Batch FK trigger checks #2951

ndeodhar opened this issue Nov 16, 2019 · 1 comment
Assignees
Labels
area/ysql Yugabyte SQL (YSQL)
Milestone

Comments

@ndeodhar
Copy link
Contributor

ndeodhar commented Nov 16, 2019

If a table being written to has a FK reference to another table, then the FK check is done as an after row trigger. After we add batching for updates or deletes in #2934 , then we should also batch FK checks for those rows.

For example, if we update 100 rows, then there will be 100 SELECT 1 from base_table WHERE id=x FOR KEY SHARE queries sent to docDB. We should batch these queries.

@ndeodhar ndeodhar added the area/ysql Yugabyte SQL (YSQL) label Nov 16, 2019
@ndeodhar ndeodhar added this to the v2.1 milestone Nov 16, 2019
@ndeodhar ndeodhar self-assigned this Nov 16, 2019
@ndeodhar ndeodhar assigned m-iancu and unassigned ndeodhar Nov 21, 2019
@ndeodhar ndeodhar modified the milestones: v2.1, v2.2.x Jun 5, 2020
@psudheer21
Copy link
Contributor

While executing the newer TPCC code with all the batching added to the NewOrder transaction we noticed that the latency for the transaction was unusually high {500ms}.
One of the writes in the transaction contains a batched insert which looks like this:

INSERT INTO ORDER_LINE (OL_O_ID, OL_D_ID, OL_W_ID, OL_NUMBER, OL_I_ID, OL_SUPPLY_W_ID, OL_QUANTITY, OL_AMOUNT, OL_DIST_INFO) VALUES (3188,2,2,1,52429,2,6,147.24000549316406,'rhkdjwimugkdparfkwpupqh '), (3188,2,2,2,96988,2,2,183.5800018310547,'zlvqwxkygnnrijapscbuvui '), (3188,2,2,3,452,2,8,385.5199890136719,'kmqfwkdgzldurgicbduvpor '), (3188,2,2,4,89191,2,3,226.95001220703125,'usqezkftahsytwriqjivaon '), (3188,2,2,5,47187,2,7,471.3799743652344,'irbepcmcdhrrferafzvqqee '), (3188,2,2,6,32101,2,4,17.31999969482422,'phmwwwyokvscstjlhwjyppb '), (3188,2,2,7,20195,2,10,387.0999755859375,'akvzmowcfxsrrxdfjoupctf'), (3188,2,2,8,92761,2,5,384.3999938964844,'fnfcondqzgqoygoqgqdzyoy '), (3188,2,2,9,99933,2,4,9.800000190734863,'hokanmhfoymbyxgjhjzlbrn '), (3188,2,2,10,48676,2,9,180.5399932861328,'lonjgnabfsvkmwvdzpmmbpy ');

The latency for this operation is about 170 ms.
This operation involves the following steps:

  1. Create the transaction
805 23:41:51.905552 16378 yb_rpc.cc:439] Call yb.tserver.TabletServerService.TakeTransaction 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 980) took 13ms. Trace:
  1. Perform the write
I0805 23:41:51.921212 15481 yb_rpc.cc:439] Call yb.tserver.TabletServerService.Write 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 982) took 14ms. Trace:
  1. Perform the foreign key validation reads:
I0805 23:41:51.934360 16171 yb_rpc.cc:439] Call yb.tserver.TabletServerService.Read 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 984) took 12ms. Trace
 109627 I0805 23:41:51.947619 16371 yb_rpc.cc:439] Call yb.tserver.TabletServerService.Read 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 985) took 12ms. Trace:
 109775 I0805 23:41:51.961211 16376 yb_rpc.cc:439] Call yb.tserver.TabletServerService.Read 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 986) took 13ms. Trace:
 109925 I0805 23:41:51.975131 16378 yb_rpc.cc:439] Call yb.tserver.TabletServerService.Read 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 987) took 13ms. Trace:
 110073 I0805 23:41:51.988736 15375 yb_rpc.cc:439] Call yb.tserver.TabletServerService.Read 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 988) took 13ms. Trace:
 110221 I0805 23:41:52.001974 16171 yb_rpc.cc:439] Call yb.tserver.TabletServerService.Read 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 989) took 12ms. Trace:
 110369 I0805 23:41:52.015722 16371 yb_rpc.cc:439] Call yb.tserver.TabletServerService.Read 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 990) took 13ms. Trace:
 110515 I0805 23:41:52.027539 16376 yb_rpc.cc:439] Call yb.tserver.TabletServerService.Read 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 991) took 11ms. Trace:
 110661 I0805 23:41:52.040786 16378 yb_rpc.cc:439] Call yb.tserver.TabletServerService.Read 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 992) took 12ms. Trace:
 110809 I0805 23:41:52.054666 15375 yb_rpc.cc:439] Call yb.tserver.TabletServerService.Read 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 993) took 13ms. Trace:
 110959 I0805 23:41:52.067888 16377 yb_rpc.cc:439] Call yb.tserver.TabletServerService.Read 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 994) took 12ms. Trace:
  1. Perform the finalization of the transaction
 110999 I0805 23:41:52.080694 15332 yb_rpc.cc:439] Call yb.tserver.TabletServerService.UpdateTransaction 172.161.41.144:45920 => 172.161.41.144:9100 (request call id 995) took 12ms. Trace:

Do note that the reads in step 3 are not parallel, rather they happen one after the other:

I0805 23:41:51.921550 16379 tablet_service.cc:1695] Received Read RPC: tablet_id: "e8b765a26cf54b348aa8a14ac3f4029d"
I0805 23:41:51.934903 16377 tablet_service.cc:1695] Received Read RPC: tablet_id: "03fcad5b9aeb45299df8341a06a86c79"
I0805 23:41:51.948118 16396 tablet_service.cc:1695] Received Read RPC: tablet_id: "a9738934304642fc9f5155dbd3360ae9"
I0805 23:41:51.961649 15400 tablet_service.cc:1695] Received Read RPC: tablet_id: "3207ccda33d8441cb6af4cd499b2b178"
I0805 23:41:51.975541 16373 tablet_service.cc:1695] Received Read RPC: tablet_id: "1dd15ba286dc4d49ac60a660d053a267"
I0805 23:41:51.989130 16374 tablet_service.cc:1695] Received Read RPC: tablet_id: "435be01faebb4931821abdb70a237685"
I0805 23:41:52.002344 16377 tablet_service.cc:1695] Received Read RPC: tablet_id: "19b593a5499e467e81f2e83a880be728"
I0805 23:41:52.016132 16396 tablet_service.cc:1695] Received Read RPC: tablet_id:"435be01faebb4931821abdb70a237685"
I0805 23:41:52.027945 16170 tablet_service.cc:1695] Received Read RPC: tablet_id:"fcb0b51383a14412a8f257ea03ce3e9b"
I0805 23:41:52.041194 16379 tablet_service.cc:1695] Received Read RPC: tablet_id:"73649bb3f9194f4cb978699a685fc6fd"
I0805 23:41:52.055063 16171 tablet_service.cc:1695] Received Read RPC: tablet_id: "c4ab87f3ce6c4645ac16215bef3e0999"

Batching the reads and performing them in parallel will drop about 120 ms from the entire operation.

@ndeodhar ndeodhar assigned psudheer21 and unassigned m-iancu Aug 6, 2020
d-uspenskiy added a commit that referenced this issue Oct 11, 2020
Summary:
Vanilla postgres processes triggers one by one. Each foreign key trigger reads individual row from the referenced table.
By knowing whole list of foreign key that should be checked it is possible to read multiple rows at a time and save it in local cache to reduce number of read RPC.

Solution:
- In the `AfterTriggerSaveEvent` function YB calculates `ybctid` of referenced row and save it in foreign key intent cache in `PgSession`
- When postgres process each trigger YB checks `ybctid` of referenced row in foreign key cache in `PgSession`
- In case no entry found YB initiates read operation of requested `ybctid` and other `ybctids` belonging to same table from `PgSession` foreign key intent cache

**Note:**
`ybctid` of row in referenced table is built from reference table tuple. In case these tables has at least one argument with different type (`int` vs `int8` for example) `ybctid` will not be built and trigger will be processed as usual without using the cache.

Speedup estimation, `release` build, `RF=1` cluster

```
CREATE TABLE parent(k INT PRIMARY KEY);
CREATE TABLE child(k INT PRIMARY KEY, v INT REFERENCES parent(k));
INSERT INTO parent SELECT s FROM generate_series(1, 100000) AS s;
\timing
INSERT INTO child SELECT s, s FROM generate_series(1, 100000) AS s;
```

Before the fix
```
Time: 37335.535 ms (00:37.336)
```

After the fix
```
Time: 2656.684 ms (00:02.657)
```

**Additional changes:**
To reduce multiple copying of `ybctid` from PgGate layer to postgres layer and back the `YBCPgYBTupleIdDescriptor` class is introduced.
It encapsulates all the data required to build `ybctid` on PgGate side. Using this class instead of `ybctid` in functions:
```
YBCPgForeignKeyReferenceCacheDelete
YBCForeignKeyReferenceExists
YBCForeignKeyReferenceIntent

```
Allows to build `ybctid` string in PgGate layer and move it to destination function in `PgSession` object without any copying.

Test Plan:
New test cases are introduced

```
./yb_build.sh --java-test org.yb.pgsql.TestPgForeignKey
./yb_build.sh --java-test org.yb.pgsql.TestPgForeignKeyBatching
```

**Note:**
New test case`org.yb.pgsql.TestPgForeignKey#testHighConcurrency` fails due to inconsistent read described it the following issue #5954
Workaround with a force flushing  of buffered operations is implemented and enabled by default. The following command can be used to run test without workaround
```
export FLAGS_TEST_disable_fk_check_force_flush=1; ./yb_build.sh --java-test org.yb.pgsql.TestPgForeignKey#testHighConcurrency
```

Reviewers: alex, sudheer, mihnea

Reviewed By: sudheer

Subscribers: zyu, yql

Differential Revision: https://phabricator.dev.yugabyte.com/D9337
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/ysql Yugabyte SQL (YSQL)
Projects
None yet
Development

No branches or pull requests

4 participants