Skip to content

Commit 20da91e

Browse files
committed
Handle Hibernate gracefully.
Hibernate gets sequence values and caches by range for assignment within hibernate, leading to potential duplicates. Make use of INCREMENT value of sequence to force updating of ms time portion.
1 parent 9a94a32 commit 20da91e

File tree

2 files changed

+72
-3
lines changed

2 files changed

+72
-3
lines changed

snowflake.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@ snowflake_nextval(PG_FUNCTION_ARGS)
172172
Buffer buf;
173173
Page page;
174174
HeapTupleData seqdatatuple;
175+
HeapTuple pgstuple;
175176
Form_pg_sequence_data seq;
177+
Form_pg_sequence pgsform;
176178
int64 result;
177179
bool logit = false;
178180
Snowflake flake;
@@ -243,9 +245,25 @@ snowflake_nextval(PG_FUNCTION_ARGS)
243245
* The clock either has not ticked or is behind. We need to make
244246
* sure that the flake doesn't move backwards and that we bump
245247
* it into the future should the count roll over.
248+
*
249+
* There is special handing for when ids are assigned in ranges
250+
* like for Hibernate. INCREMENT as part of the sequence definition
251+
* can be used. If the count portion of snowflake exceeds the
252+
* count mask + 1 (4096), it will cause the count to be set to 0
253+
* and the time ms portion is incremented.
246254
*/
247-
flake.sf_count++;
248-
if ((flake.sf_count & SNOWFLAKE_COUNT_MASK) == 0)
255+
256+
pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
257+
if (HeapTupleIsValid(pgstuple))
258+
{
259+
pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
260+
elm->increment = pgsform->seqincrement;
261+
ReleaseSysCache(pgstuple);
262+
}
263+
flake.sf_count += elm->increment;
264+
265+
/* Mask uses least signicant bits, so this is safe */
266+
if (flake.sf_count > SNOWFLAKE_COUNT_MASK)
249267
{
250268
flake.sf_count = 0;
251269
flake.sf_msec++;
@@ -533,6 +551,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
533551
elm->lxid = InvalidLocalTransactionId;
534552
elm->last_valid = false;
535553
elm->last = elm->cached = 0;
554+
elm->increment = 1;
536555
}
537556

538557
/*

sql/conversion.sql

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,57 @@ INSERT INTO t5 values (DEFAULT);
9999
SELECT x AS sf_val FROM t4 WHERE x > 42 \gset
100100
SELECT :sf_val < x AS is_growing FROM t5 WHERE x > 42;
101101

102+
-- Test INCREMENT
103+
CREATE TABLE t6(x bigserial, y int);
104+
INSERT INTO t6 VALUES (DEFAULT, 10);
105+
INSERT INTO t6 VALUES (DEFAULT, 20);
106+
SELECT * FROM t6;
107+
INSERT INTO t6 VALUES (DEFAULT, 30);
108+
SELECT * FROM t6;
109+
SELECT snowflake.convert_sequence_to_snowflake('t6_x_seq');
110+
-- Get the count portion of the id.
111+
-- It happens within the same milisecond, should increment
112+
SELECT snowflake.get_count(snowflake.nextval('t6_x_seq'))
113+
UNION ALL
114+
SELECT snowflake.get_count(snowflake.nextval('t6_x_seq'))
115+
UNION ALL
116+
SELECT snowflake.get_count(snowflake.nextval('t6_x_seq'));
117+
118+
-- Should return 1 (the counter difference)
119+
SELECT ABS(snowflake.nextval('t6_x_seq') - snowflake.nextval('t6_x_seq'));
120+
121+
-- If < 4096, will increment that amount
122+
ALTER SEQUENCE t6_x_seq INCREMENT 100 NO MAXVALUE;
123+
SELECT snowflake.get_count(snowflake.nextval('t6_x_seq'))
124+
UNION ALL
125+
SELECT snowflake.get_count(snowflake.nextval('t6_x_seq'))
126+
UNION ALL
127+
SELECT snowflake.get_count(snowflake.nextval('t6_x_seq'));
128+
129+
-- This will force the time ms portion to increment,
130+
-- the count portion should be 0
131+
ALTER SEQUENCE t6_x_seq INCREMENT 4096;
132+
SELECT snowflake.get_count(snowflake.nextval('t6_x_seq'))
133+
UNION ALL
134+
SELECT snowflake.get_count(snowflake.nextval('t6_x_seq'))
135+
UNION ALL
136+
SELECT snowflake.get_count(snowflake.nextval('t6_x_seq'));
137+
138+
-- should return 1 (the ms difference)
139+
SELECT ABS(snowflake.nextval('t6_x_seq') - snowflake.nextval('t6_x_seq')) >> 22;
140+
141+
-- Test unreasonable value- still should get 0s
142+
ALTER SEQUENCE t6_x_seq INCREMENT 9999;
143+
SELECT snowflake.get_count(snowflake.nextval('t6_x_seq'))
144+
UNION ALL
145+
SELECT snowflake.get_count(snowflake.nextval('t6_x_seq'))
146+
UNION ALL
147+
SELECT snowflake.get_count(snowflake.nextval('t6_x_seq'));
148+
149+
-- should return 1 (the ms difference)
150+
SELECT ABS(snowflake.nextval('t6_x_seq') - snowflake.nextval('t6_x_seq')) >> 22;
151+
102152
-- Cleanup
103-
DROP TABLE t1,t2,t3,t4,t5 CASCADE;
153+
DROP TABLE t1,t2,t3,t4,t5,t6 CASCADE;
104154
DROP SEQUENCE favorite_seq;
105155
DROP EXTENSION snowflake;

0 commit comments

Comments
 (0)