new_customer
웹 응용 프로그램에서 내 함수를 초당 여러 번 (세션 당 한 번만) 호출합니다. 가장 먼저하는 일은 customer
테이블을 잠그는 것입니다 ( '존재하지 않는 경우 삽입'-간단한 변형 upsert
).
문서 에 대한 나의 이해는 new_customer
이전의 모든 호출이 완료 될 때까지 다른 호출 이 단순히 대기해야한다는 것입니다.
LOCK TABLE은 충돌하는 잠금이 해제 될 때까지 대기하면서 테이블 수준 잠금을 얻습니다.
왜 대신 교착 상태가 발생합니까?
정의:
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;
$$;
로그 오류 :
BST 세부 정보 : 프로세스 12380은 데이터베이스 12141의 16438 관계에서 ExclusiveLock을 기다립니다. 프로세스 12379에 의해 차단되었습니다. 프로세스 12379는 데이터베이스 12141의 관계 16438에서 ExclusiveLock을 대기합니다. 프로세스 12380에 의해 차단되었습니다. 프로세스 12380 : new_customer (decode ($ 1 :: text, 'hex')) 선택 프로세스 12379 : new_customer (decode ($ 1 :: text, 'hex')) 선택 2015-07-28 08:02:58 BST 힌트 : 쿼리 세부 정보는 서버 로그를 참조하십시오. 2015-07-28 08:02:58 BST 컨텍스트 : SQL 함수 "new_customer"문 1 2015-07-28 08:02:58 BST 진술 : new_customer (decode ($ 1 :: text, 'hex') 선택)
관계:
postgres=# select relname from pg_class where oid=16438;
┌──────────┐
│ relname │
├──────────┤
│ customer │
└──────────┘
편집하다:
나는 간단하고 재현 가능한 테스트 사례를 얻었습니다. 나에게 이것은 일종의 경쟁 조건으로 인해 버그처럼 보입니다.
개요:
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 스크립트는 두 개의 bash 세션에서 동시에 실행됩니다.
for i in {1..1000}; do psql postgres postgres -c "select f_test('blah')"; done
오류 로그 (보통 1000 개가 넘는 소수의 교착 상태) :
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')
편집 2 :
@ypercube 는 함수 외부의 변형 을 제안했습니다lock table
.
for i in {1..1000}; do psql postgres postgres -c "begin; lock test in exclusive mode; select f_test('blah'); end"; done
흥미롭게도 교착 상태가 제거됩니다.
customer
약한 잠금을 잡는 방식으로 사용됩니까? 그러면 잠금 업그레이드 문제 일 수 있습니다.