Skip to content

Instantly share code, notes, and snippets.

@fabiolimace
Last active April 25, 2024 04:23
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fabiolimace/6d8d2a4abf67d54d025eca26bcbd1cde to your computer and use it in GitHub Desktop.
Save fabiolimace/6d8d2a4abf67d54d025eca26bcbd1cde to your computer and use it in GitHub Desktop.
Function for generating Time Sortable ID with millisecond precision on PostgreSQL
/**
* Returns a Time Sortable ID with millisecond precision.
*
* Time component: 42 bits (2^42 = ~69 years)
*
* Random component: 22 bits (2^22 = 4,194,304)
*
* The time component is the count of milliseconds since 2020-01-01T00:00:00Z.
*
* Tags: tsid ulid snowflake id-generator generator time sortable sort order id
*/
create or replace function public.fn_tsid_milli() returns bigint as $$
declare
-- Milliseconds precision
C_MILLI_PREC bigint := 10^3;
-- Random component bit length: 22 bits
C_RANDOM_LEN bigint := 2^22;
-- TSID epoch: seconds since 2020-01-01Z
-- extract(epoch from '2020-01-01'::date)
C_TSID_EPOCH bigint := 1577836800;
begin
return ((floor((extract('epoch' from clock_timestamp()) - C_TSID_EPOCH) * C_MILLI_PREC) * C_RANDOM_LEN)::bigint) + (floor(random() * C_RANDOM_LEN)::bigint);
end $$ language plpgsql;
-- EXAMPLE 1:
-- select fn_tsid_milli();
-- EXAMPLE 1 OUTPUT:
-- 52930646021833201
----------------------------------------------------------
/*
---------------------
-- LOOP TEST
---------------------
-- Insert many into a test table.
-- FIXME:
-- It fails when the same TSID is inserted more than once.
-- It's necessary to figure out a solution, maybe an auxiliar state table with exclusive lock.
-- The function 'fn_tsid_MICRO()' is less prone to this fail since it has microsecond precision.
---------------------
do $$
declare
i int := 0;
lim int := 1000000;
begin
WHILE i < lim LOOP
insert into public.tb_tsid_test(id) values (public.fn_tsid_milli());
i := i + 1;
END LOOP;
end $$ language plpgsql;
*/
-- CREATE TEST TABLE
-- create table public.tb_tsid_test (id bigint unique);
-- CHECK RECORDS
-- select * from public.tb_tsid_test;
-- CLEAR RECORDS
-- delete from public.tb_tsid_test;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment