mirror of https://github.com/citusdata/citus.git
380 lines
12 KiB
Plaintext
380 lines
12 KiB
Plaintext
CREATE SCHEMA clock;
|
|
SET search_path TO clock;
|
|
SHOW citus.enable_cluster_clock;
|
|
citus.enable_cluster_clock
|
|
---------------------------------------------------------------------
|
|
on
|
|
(1 row)
|
|
|
|
SET citus.enable_cluster_clock to ON;
|
|
SHOW citus.enable_cluster_clock;
|
|
citus.enable_cluster_clock
|
|
---------------------------------------------------------------------
|
|
on
|
|
(1 row)
|
|
|
|
CREATE TABLE clock_test (id int, nonid int);
|
|
SELECT create_distributed_table('clock_test', 'id', colocate_with := 'none');
|
|
create_distributed_table
|
|
---------------------------------------------------------------------
|
|
|
|
(1 row)
|
|
|
|
--
|
|
-- Compare <logical, counter> pairs
|
|
--
|
|
-- Returns true
|
|
SELECT citus_is_clock_after('(5,1)', '(3,6)');
|
|
citus_is_clock_after
|
|
---------------------------------------------------------------------
|
|
t
|
|
(1 row)
|
|
|
|
-- Returns false
|
|
SELECT citus_is_clock_after('(2,9)', '(3,0)');
|
|
citus_is_clock_after
|
|
---------------------------------------------------------------------
|
|
f
|
|
(1 row)
|
|
|
|
-- Returns true
|
|
SELECT citus_is_clock_after('(5,6)', '(5,1)');
|
|
citus_is_clock_after
|
|
---------------------------------------------------------------------
|
|
t
|
|
(1 row)
|
|
|
|
-- Returns false
|
|
SELECT citus_is_clock_after('(5,6)', '(5,6)');
|
|
citus_is_clock_after
|
|
---------------------------------------------------------------------
|
|
f
|
|
(1 row)
|
|
|
|
--
|
|
-- Check the clock is *monotonically increasing*
|
|
--
|
|
SELECT citus_get_node_clock() \gset t1
|
|
SELECT citus_get_node_clock() \gset t2
|
|
SELECT citus_get_node_clock() \gset t3
|
|
-- Both should return true
|
|
SELECT citus_is_clock_after(:'t2citus_get_node_clock', :'t1citus_get_node_clock');
|
|
citus_is_clock_after
|
|
---------------------------------------------------------------------
|
|
t
|
|
(1 row)
|
|
|
|
SELECT citus_is_clock_after(:'t3citus_get_node_clock', :'t2citus_get_node_clock');
|
|
citus_is_clock_after
|
|
---------------------------------------------------------------------
|
|
t
|
|
(1 row)
|
|
|
|
-- Returns false
|
|
SELECT citus_is_clock_after(:'t1citus_get_node_clock', :'t3citus_get_node_clock');
|
|
citus_is_clock_after
|
|
---------------------------------------------------------------------
|
|
f
|
|
(1 row)
|
|
|
|
CREATE TABLE cluster_clock_type(cc cluster_clock);
|
|
INSERT INTO cluster_clock_type values('(0, 100)');
|
|
INSERT INTO cluster_clock_type values('(100, 0)');
|
|
INSERT INTO cluster_clock_type values('(100, 1)');
|
|
INSERT INTO cluster_clock_type values('(100, 2)');
|
|
INSERT INTO cluster_clock_type values('(100, 200)');
|
|
INSERT INTO cluster_clock_type values('(100, 100)');
|
|
INSERT INTO cluster_clock_type values('(200, 20)');
|
|
INSERT INTO cluster_clock_type values('(200, 3)');
|
|
INSERT INTO cluster_clock_type values('(200, 400)');
|
|
INSERT INTO cluster_clock_type values('(500, 600)');
|
|
INSERT INTO cluster_clock_type values('(500, 0)');
|
|
SELECT cc FROM cluster_clock_type ORDER BY 1 ASC;
|
|
cc
|
|
---------------------------------------------------------------------
|
|
(0,100)
|
|
(100,0)
|
|
(100,1)
|
|
(100,2)
|
|
(100,100)
|
|
(100,200)
|
|
(200,3)
|
|
(200,20)
|
|
(200,400)
|
|
(500,0)
|
|
(500,600)
|
|
(11 rows)
|
|
|
|
SELECT cc FROM cluster_clock_type where cc = '(200, 400)';
|
|
cc
|
|
---------------------------------------------------------------------
|
|
(200,400)
|
|
(1 row)
|
|
|
|
SELECT cc FROM cluster_clock_type where cc <> '(500, 600)';
|
|
cc
|
|
---------------------------------------------------------------------
|
|
(0,100)
|
|
(100,0)
|
|
(100,1)
|
|
(100,2)
|
|
(100,200)
|
|
(100,100)
|
|
(200,20)
|
|
(200,3)
|
|
(200,400)
|
|
(500,0)
|
|
(10 rows)
|
|
|
|
SELECT cc FROM cluster_clock_type where cc != '(500, 600)';
|
|
cc
|
|
---------------------------------------------------------------------
|
|
(0,100)
|
|
(100,0)
|
|
(100,1)
|
|
(100,2)
|
|
(100,200)
|
|
(100,100)
|
|
(200,20)
|
|
(200,3)
|
|
(200,400)
|
|
(500,0)
|
|
(10 rows)
|
|
|
|
SELECT cc FROM cluster_clock_type where cc < '(200, 20)' ORDER BY 1 ASC;
|
|
cc
|
|
---------------------------------------------------------------------
|
|
(0,100)
|
|
(100,0)
|
|
(100,1)
|
|
(100,2)
|
|
(100,100)
|
|
(100,200)
|
|
(200,3)
|
|
(7 rows)
|
|
|
|
SELECT cc FROM cluster_clock_type where cc <= '(200, 20)' ORDER BY 1 ASC;
|
|
cc
|
|
---------------------------------------------------------------------
|
|
(0,100)
|
|
(100,0)
|
|
(100,1)
|
|
(100,2)
|
|
(100,100)
|
|
(100,200)
|
|
(200,3)
|
|
(200,20)
|
|
(8 rows)
|
|
|
|
SELECT cc FROM cluster_clock_type where cc > '(200, 20)' ORDER BY 1 ASC;
|
|
cc
|
|
---------------------------------------------------------------------
|
|
(200,400)
|
|
(500,0)
|
|
(500,600)
|
|
(3 rows)
|
|
|
|
SELECT cc FROM cluster_clock_type where cc >= '(200, 20)' ORDER BY 1 ASC;
|
|
cc
|
|
---------------------------------------------------------------------
|
|
(200,20)
|
|
(200,400)
|
|
(500,0)
|
|
(500,600)
|
|
(4 rows)
|
|
|
|
CREATE INDEX cc_idx on cluster_clock_type(cc);
|
|
-- Multiply rows to check index usage
|
|
INSERT INTO cluster_clock_type SELECT a.cc FROM cluster_clock_type a, cluster_clock_type b;
|
|
INSERT INTO cluster_clock_type SELECT a.cc FROM cluster_clock_type a, cluster_clock_type b;
|
|
EXPLAIN SELECT cc FROM cluster_clock_type ORDER BY 1 ASC LIMIT 1;
|
|
QUERY PLAN
|
|
---------------------------------------------------------------------
|
|
Limit (cost=0.28..0.92 rows=1 width=12)
|
|
-> Index Only Scan using cc_idx on cluster_clock_type (cost=0.28..667.95 rows=1045 width=12)
|
|
(2 rows)
|
|
|
|
SELECT cc FROM cluster_clock_type ORDER BY 1 ASC LIMIT 1;
|
|
cc
|
|
---------------------------------------------------------------------
|
|
(0,100)
|
|
(1 row)
|
|
|
|
EXPLAIN SELECT cc FROM cluster_clock_type where cc = '(200, 20)' LIMIT 5;
|
|
QUERY PLAN
|
|
---------------------------------------------------------------------
|
|
Limit (cost=4.32..20.94 rows=5 width=12)
|
|
-> Bitmap Heap Scan on cluster_clock_type (cost=4.32..20.94 rows=5 width=12)
|
|
Recheck Cond: (cc = '(200,20)'::cluster_clock)
|
|
-> Bitmap Index Scan on cc_idx (cost=0.00..4.31 rows=5 width=0)
|
|
Index Cond: (cc = '(200,20)'::cluster_clock)
|
|
(5 rows)
|
|
|
|
SELECT cc FROM cluster_clock_type where cc = '(200, 20)' LIMIT 5;
|
|
cc
|
|
---------------------------------------------------------------------
|
|
(200,20)
|
|
(200,20)
|
|
(200,20)
|
|
(200,20)
|
|
(200,20)
|
|
(5 rows)
|
|
|
|
-- Max limits
|
|
INSERT INTO cluster_clock_type values('(4398046511103, 0)');
|
|
INSERT INTO cluster_clock_type values('(0, 4194303)');
|
|
INSERT INTO cluster_clock_type values('(4398046511103, 4194303)');
|
|
-- Bad input
|
|
INSERT INTO cluster_clock_type values('(-1, 100)');
|
|
ERROR: invalid input syntax for type cluster_clock: "(-1, 100)"
|
|
INSERT INTO cluster_clock_type values('(100, -1)');
|
|
ERROR: invalid input syntax for type cluster_clock: "(100, -1)"
|
|
INSERT INTO cluster_clock_type values('(4398046511104, 100)'); -- too big to fit into 42 bits
|
|
ERROR: invalid input syntax for type cluster_clock: "(4398046511104, 100)"
|
|
INSERT INTO cluster_clock_type values('(0, 4194304)'); -- too big to fit into 22 bits
|
|
ERROR: invalid input syntax for type cluster_clock: "(0, 4194304)"
|
|
DROP TABLE cluster_clock_type;
|
|
CREATE TABLE cluster_clock_type(cc cluster_clock UNIQUE);
|
|
INSERT INTO cluster_clock_type values('(100, 1)');
|
|
INSERT INTO cluster_clock_type values('(100, 1)');
|
|
ERROR: duplicate key value violates unique constraint "cluster_clock_type_cc_key"
|
|
DETAIL: Key (cc)=((100,1)) already exists.
|
|
INSERT INTO cluster_clock_type values('(100, 200)');
|
|
INSERT INTO cluster_clock_type values('(100, 200)');
|
|
ERROR: duplicate key value violates unique constraint "cluster_clock_type_cc_key"
|
|
DETAIL: Key (cc)=((100,200)) already exists.
|
|
INSERT INTO cluster_clock_type values('(100, 100)');
|
|
INSERT INTO cluster_clock_type values('(100, 100)');
|
|
ERROR: duplicate key value violates unique constraint "cluster_clock_type_cc_key"
|
|
DETAIL: Key (cc)=((100,100)) already exists.
|
|
--
|
|
-- Check the value returned by citus_get_node_clock is close to epoch in ms
|
|
--
|
|
SELECT (extract(epoch from now()) * 1000)::bigint AS epoch,
|
|
citus_get_node_clock() AS latest_clock \gset
|
|
-- Returns difference in epoch-milliseconds
|
|
SELECT CASE WHEN msdiff BETWEEN 0 AND 25 THEN 0 ELSE msdiff END
|
|
FROM ABS(:epoch - cluster_clock_logical(:'latest_clock')) msdiff;
|
|
msdiff
|
|
---------------------------------------------------------------------
|
|
0
|
|
(1 row)
|
|
|
|
BEGIN;
|
|
SELECT citus_get_transaction_clock();
|
|
citus_get_transaction_clock
|
|
---------------------------------------------------------------------
|
|
(xxxxxxxxxxxxx,x)
|
|
(1 row)
|
|
|
|
END;
|
|
-- Transaction that accesses multiple nodes
|
|
BEGIN;
|
|
INSERT INTO clock_test SELECT generate_series(1, 10000, 1), 0;
|
|
SELECT get_current_transaction_id() \gset tid
|
|
SET client_min_messages TO DEBUG1;
|
|
-- Capture the transaction timestamp
|
|
SELECT citus_get_transaction_clock() as txnclock \gset
|
|
DEBUG: node xxxx transaction clock xxxxxx
|
|
DEBUG: node xxxx transaction clock xxxxxx
|
|
DEBUG: node xxxx transaction clock xxxxxx
|
|
DEBUG: final global transaction clock xxxxxx
|
|
COMMIT;
|
|
--
|
|
-- Check to see if the clock is persisted in the sequence.
|
|
--
|
|
SELECT result as logseq from run_command_on_workers($$SELECT last_value FROM pg_dist_clock_logical_seq$$) limit 1 \gset
|
|
SELECT cluster_clock_logical(:'txnclock') as txnlog \gset
|
|
SELECT :logseq = :txnlog;
|
|
?column?
|
|
---------------------------------------------------------------------
|
|
t
|
|
(1 row)
|
|
|
|
BEGIN;
|
|
INSERT INTO clock_test SELECT generate_series(1, 10000, 1), 0;
|
|
DEBUG: distributed INSERT ... SELECT can only select from distributed tables
|
|
DEBUG: Collecting INSERT ... SELECT results on coordinator
|
|
SELECT get_current_transaction_id() \gset tid
|
|
SET client_min_messages TO DEBUG1;
|
|
-- Capture the transaction timestamp
|
|
SELECT citus_get_transaction_clock() as txnclock \gset
|
|
DEBUG: node xxxx transaction clock xxxxxx
|
|
DEBUG: node xxxx transaction clock xxxxxx
|
|
DEBUG: node xxxx transaction clock xxxxxx
|
|
DEBUG: final global transaction clock xxxxxx
|
|
ROLLBACK;
|
|
SELECT result as logseq from run_command_on_workers($$SELECT last_value FROM pg_dist_clock_logical_seq$$) limit 1 \gset
|
|
SELECT cluster_clock_logical(:'txnclock') as txnlog \gset
|
|
SELECT :logseq = :txnlog;
|
|
?column?
|
|
---------------------------------------------------------------------
|
|
t
|
|
(1 row)
|
|
|
|
SELECT run_command_on_workers($$SELECT citus_get_node_clock()$$);
|
|
run_command_on_workers
|
|
---------------------------------------------------------------------
|
|
(localhost, xxx,t,"(xxxxxxxxxxxxx,x)")
|
|
(localhost, xxx,t,"(xxxxxxxxxxxxx,x)")
|
|
(2 rows)
|
|
|
|
SET citus.enable_cluster_clock to OFF;
|
|
BEGIN;
|
|
SELECT citus_get_transaction_clock();
|
|
WARNING: GUC enable_cluster_clock is off
|
|
citus_get_transaction_clock
|
|
---------------------------------------------------------------------
|
|
|
|
(1 row)
|
|
|
|
END;
|
|
SET citus.enable_cluster_clock to ON;
|
|
-- Test if the clock UDFs are volatile, result should never be the same
|
|
SELECT citus_get_node_clock() = citus_get_node_clock();
|
|
?column?
|
|
---------------------------------------------------------------------
|
|
f
|
|
(1 row)
|
|
|
|
select citus_get_transaction_clock() = citus_get_transaction_clock();
|
|
DEBUG: node xxxx transaction clock xxxxxx
|
|
DEBUG: final global transaction clock xxxxxx
|
|
DEBUG: node xxxx transaction clock xxxxxx
|
|
DEBUG: final global transaction clock xxxxxx
|
|
?column?
|
|
---------------------------------------------------------------------
|
|
f
|
|
(1 row)
|
|
|
|
-- Test if the clock UDFs are usable by non-superusers
|
|
CREATE ROLE non_super_user_clock;
|
|
SET ROLE non_super_user_clock;
|
|
SELECT citus_get_node_clock();
|
|
citus_get_node_clock
|
|
---------------------------------------------------------------------
|
|
(xxxxxxxxxxxxx,x)
|
|
(1 row)
|
|
|
|
BEGIN;
|
|
SELECT citus_get_transaction_clock();
|
|
DEBUG: node xxxx transaction clock xxxxxx
|
|
DEBUG: final global transaction clock xxxxxx
|
|
citus_get_transaction_clock
|
|
---------------------------------------------------------------------
|
|
(xxxxxxxxxxxxx,x)
|
|
(1 row)
|
|
|
|
COMMIT;
|
|
-- Test setting the persisted clock value (it must fail)
|
|
SELECT setval('pg_dist_clock_logical_seq', 100, true);
|
|
ERROR: permission denied for sequence pg_dist_clock_logical_seq
|
|
\c
|
|
RESET client_min_messages;
|
|
RESET citus.enable_cluster_clock;
|
|
DROP ROLE non_super_user_clock;
|
|
DROP SCHEMA clock CASCADE;
|
|
NOTICE: drop cascades to 2 other objects
|
|
DETAIL: drop cascades to table clock.clock_test
|
|
drop cascades to table clock.cluster_clock_type
|