heterodb / pg-strom

PG-Strom - Master development repository
http://heterodb.github.io/pg-strom/
Other
1.3k stars 162 forks source link

PG17:fallback発生時(?)LIKE条件の絞り込み結果が異なる。 #819

Closed 0-kaz closed 1 week ago

0-kaz commented 3 months ago

fallback_pgsql.sqlの動作確認で発見。

確認コミット:fd4c07d7c36dc190d9ab99b3d56d5d3b2bcd9405

以下再現クエリ。

DROP TABLE IF EXISTS testdata;
CREATE TABLE testdata (
  id    int,
  memo  text
);
SELECT pgstrom.random_setseed(20190714);
INSERT INTO testdata (
  SELECT x, pgstrom.random_text_len(2, 200)
    FROM generate_series(1,400001) x);
UPDATE testdata
   SET memo = md5(memo) || md5(memo)
 WHERE id = 400001;
UPDATE testdata
   SET memo = memo || '-' || memo || '-' || memo || '-' || memo
 WHERE id = 400001;
UPDATE testdata
   SET memo = memo || '-' || memo || '-' || memo || '-' || memo
 WHERE id = 400001;
UPDATE testdata
   SET memo = memo || '-' || memo || '-' || memo || '-' || memo
 WHERE id = 400001;

SET pg_strom.enabled = on;
SET pg_strom.cpu_fallback = on;
SELECT count(id) FROM testdata
 WHERE memo LIKE '%abc%';                 -- 結果は163件
SET pg_strom.enabled = off;
SELECT count(id) FROM testdata
 WHERE memo LIKE '%abc%';                 -- 結果は164件
kaigai commented 2 months ago

Fallback処理の場合、executor.ctryExecCpuFallbackChunksという関数でfallback行をフェッチされるはずなので、 まずは、この関数が呼び出されているのか(⇒呼び出されてなければ、fallbackバッファが返されてない)、 呼び出されていれば、そもそも行が取り出されているのかをチェックしてみると良いかもしれません。

0-kaz commented 1 month ago

Memo: PG16でも発生。

SET pg_strom.enabled = on;
SET pg_strom.cpu_fallback = on;
SELECT id INTO gpuresult FROM testdata WHERE memo LIKE '%abc%';

SET pg_strom.enabled = off;
SELECT id INTO cpuresult FROM testdata WHERE memo LIKE '%abc%';

(SELECT * FROM gpuresult EXCEPT ALL SELECT * FROM cpuresult) order by id;

結果:

  id   
-------
   573
   573
  6642
  6656
  9545
 13427
 15693
 20634
(8 rows)

元テーブルの結果をselect→insertしているだけなのに、ユニークなはずのIDが重複している。 fallbackの過程でデータが不正な状態となっている可能性が大きい。

select * from gpuresult where id=573;
 id  
-----
 573
 573
 573
(3 rows)
kaigai commented 2 weeks ago

比較的簡単そうと思って振りましたが、めちゃくちゃエグいバグでした…。

54176d243f0a9167bac5097b9e87f91ac585aec4 で修正していますが、発生の機序はこうです。

  1. GpuScan(ROW/ARROW/COLUMN) では、wp->smx_row_countを元に、各スレッドがフェッチする行を特定する。
  2. wp->smx_row_countに係数を掛けて行インデックスを確定した後、即座にwp->smx_row_countをインクリメントする
  3. 行インデックスを元に取りだした行がCPU fallbackを発生させる
  4. 初回は fallback buffer を持っていないので、いったんGPU kernelをsuspendさせる。
  5. GPU kernelをresumeした後、再度、wp->smx_row_countに係数を掛けて行インデックスを計算する
  6. しかし、このwp->smx_row_countは更新後の値なので、通常は512行が読み飛ばされてしまう。⇒ 結果不正合

ちなみに、KDS_FORMAT_BLOCKの場合だけは、たまたま、CPU-Fallbackの可能性が無くなってからwp->smx_row_countを更新するという構造になっていたので、pg_strom.gpudirect_enabledのオンオフで当該現象が切り替わるというように見えていたようです。

postgres=# SET pg_strom.gpudirect_enabled = on;
SET
postgres=# SELECT count(id) FROM testdata WHERE memo LIKE '%abc%';
NOTICE:  GpuPreAgg: CPU fallback 1 tuples (144B)
 count
-------
   164
(1 row)

postgres=# SET pg_strom.gpudirect_enabled = off;
SET
postgres=# SELECT count(id) FROM testdata WHERE memo LIKE '%abc%';
NOTICE:  GpuPreAgg: CPU fallback 1 tuples (144B)
 count
-------
   164
(1 row)
0-kaz commented 1 week ago

ありがとうございます。解消確認出来ました。 PG16、PG17の両方で確認。