Chức năng của tôi new_customer
được một ứng dụng web gọi vài lần mỗi giây (nhưng chỉ một lần mỗi phiên). Điều đầu tiên nó làm là khóa customer
bảng (để thực hiện 'chèn nếu không tồn tại' một biến thể đơn giản của một upsert
).
Sự hiểu biết của tôi về các tài liệu là các cuộc gọi khác new_customer
chỉ nên xếp hàng cho đến khi tất cả các cuộc gọi trước kết thúc:
LOCK TABLE có được một khóa cấp bảng, chờ đợi nếu cần thiết cho bất kỳ khóa xung đột nào được phát hành.
Tại sao đôi khi nó bế tắc thay thế?
Định nghĩa:
create function new_customer(secret bytea) returns integer language sql
security definer set search_path = postgres,pg_temp as $$
lock customer in exclusive mode;
--
with w as ( insert into customer(customer_secret,customer_read_secret)
select secret,decode(md5(encode(secret, 'hex')),'hex')
where not exists(select * from customer where customer_secret=secret)
returning customer_id )
insert into collection(customer_id) select customer_id from w;
--
select customer_id from customer where customer_secret=secret;
$$;
lỗi từ nhật ký:
2015-07-28 08:02:58 BST CHI TIẾT: Quá trình 12380 chờ đợi ExclusiveLock về mối quan hệ 16438 của cơ sở dữ liệu 12141; bị chặn bởi quá trình 12379. Quá trình 12379 chờ ExclusiveLock về mối quan hệ 16438 của cơ sở dữ liệu 12141; bị chặn bởi quá trình 12380. Quá trình 12380: chọn new_customer (giải mã ($ 1 :: văn bản, 'hex')) Quy trình 12379: chọn new_customer (giải mã ($ 1 :: văn bản, 'hex')) 2015-07-28 08:02:58 BST GỢI Ý: Xem nhật ký máy chủ để biết chi tiết truy vấn. 2015-07-28 08:02:58 BST TIẾP THEO: Câu lệnh SQL "new_customer" 1 2015-07-28 08:02:58 TUYÊN BỐ BST: chọn new_customer (giải mã ($ 1 :: văn bản, 'hex'))
quan hệ:
postgres=# select relname from pg_class where oid=16438;
┌──────────┐
│ relname │
├──────────┤
│ customer │
└──────────┘
biên tập:
Tôi đã quản lý để có được một trường hợp thử nghiệm tái sản xuất đơn giản. Đối với tôi điều này trông giống như một lỗi do một số điều kiện cuộc đua.
lược đồ:
create table test( id serial primary key, val text );
create function f_test(v text) returns integer language sql security definer set search_path = postgres,pg_temp as $$
lock test in exclusive mode;
insert into test(val) select v where not exists(select * from test where val=v);
select id from test where val=v;
$$;
bash script chạy đồng thời trong hai phiên bash:
for i in {1..1000}; do psql postgres postgres -c "select f_test('blah')"; done
nhật ký lỗi (thường là một số ít các bế tắc trong 1000 cuộc gọi):
2015-07-28 16:46:19 BST ERROR: deadlock detected
2015-07-28 16:46:19 BST DETAIL: Process 9394 waits for ExclusiveLock on relation 65605 of database 12141; blocked by process 9393.
Process 9393 waits for ExclusiveLock on relation 65605 of database 12141; blocked by process 9394.
Process 9394: select f_test('blah')
Process 9393: select f_test('blah')
2015-07-28 16:46:19 BST HINT: See server log for query details.
2015-07-28 16:46:19 BST CONTEXT: SQL function "f_test" statement 1
2015-07-28 16:46:19 BST STATEMENT: select f_test('blah')
chỉnh sửa 2:
@ypercube đề xuất một biến thể với lock table
chức năng bên ngoài:
for i in {1..1000}; do psql postgres postgres -c "begin; lock test in exclusive mode; select f_test('blah'); end"; done
thú vị là điều này giúp loại bỏ những bế tắc.
customer
sử dụng theo cách nào sẽ lấy khóa yếu hơn? Sau đó, nó có thể là một vấn đề nâng cấp khóa.