Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .unreleased/pr_9964
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implements: #9964 Add a function to lock OSM chunk's dimension slice
9 changes: 9 additions & 0 deletions sql/osm_api.sql
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,12 @@ CREATE OR REPLACE FUNCTION _timescaledb_functions.hypertable_osm_range_update(
empty BOOL = false
) RETURNS BOOL AS '@MODULE_PATHNAME@',
'ts_hypertable_osm_range_update' LANGUAGE C VOLATILE;

-- Acquires a FOR UPDATE row lock on the dimension slice tuple belonging to the
-- OSM chunk of the given hypertable. There is exactly one OSM chunk per
-- hypertable, so this locks its single dimension_slice entry. The lock is held
-- until the end of the current transaction; nothing is returned.
CREATE OR REPLACE FUNCTION _timescaledb_functions.lock_osm_chunk_dimension_slice(
htoid REGCLASS
) RETURNS VOID AS '@MODULE_PATHNAME@',
'ts_lock_osm_chunk_dimension_slice' LANGUAGE C VOLATILE;
2 changes: 2 additions & 0 deletions sql/updates/reverse-dev.sql
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ $$;
DROP VIEW IF EXISTS _timescaledb_catalog.chunk_constraint;
DROP FUNCTION IF EXISTS _timescaledb_functions.chunk_constraint_add_table_constraint( integer, name, name);

DROP FUNCTION IF EXISTS _timescaledb_functions.lock_osm_chunk_dimension_slice(regclass);

ALTER TABLE _timescaledb_catalog.hypertable RESET (user_catalog_table);
ALTER TABLE _timescaledb_catalog.chunk RESET (user_catalog_table);

Expand Down
65 changes: 65 additions & 0 deletions src/hypertable.c
Original file line number Diff line number Diff line change
Expand Up @@ -2690,6 +2690,71 @@ ts_hypertable_osm_range_update(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(overlap);
}

/*
* lock_osm_chunk_dimension_slice
* 0 hypertable REGCLASS
*
* Acquires a FOR UPDATE row lock on the dimension slice tuple belonging to the
* OSM chunk of the given hypertable. There is exactly one OSM chunk per
* hypertable, so this locks its single dimension slice entry. The lock is held
* until the end of the current transaction. Returns void.
*
* Like hypertable_osm_range_update this is meant to be used by OSM to
* coordinate access to the OSM chunk's dimension slice; it is not meant to run
* on a read-only secondary.
*/
TS_FUNCTION_INFO_V1(ts_lock_osm_chunk_dimension_slice);
Datum
ts_lock_osm_chunk_dimension_slice(PG_FUNCTION_ARGS)
{
Oid relid = PG_ARGISNULL(0) ? InvalidOid : PG_GETARG_OID(0);
Hypertable *ht;
const Dimension *time_dim;
Cache *hcache;

Assert(!RecoveryInProgress());

hcache = ts_hypertable_cache_pin();
ht = ts_resolve_hypertable_from_table_or_cagg(hcache, relid, true);
Assert(ht != NULL);
time_dim = hyperspace_get_open_dimension(ht->space, 0);

Ensure(time_dim != NULL,
"could not find time dimension for hypertable %s.%s",
quote_identifier(NameStr(ht->fd.schema_name)),
quote_identifier(NameStr(ht->fd.table_name)));

int32 osm_chunk_id = ts_chunk_get_osm_chunk_id(ht->fd.id);
if (osm_chunk_id == INVALID_CHUNK_ID)
{
ereport(ERROR,
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("no OSM chunk found for hypertable %s.%s",
quote_identifier(NameStr(ht->fd.schema_name)),
quote_identifier(NameStr(ht->fd.table_name))));
}

/*
* Lock the OSM chunk's dimension slice tuple FOR UPDATE. The row lock is
* held until the end of the current transaction.
*/
DimensionSlice *slice = ts_chunk_get_osm_slice_and_lock(osm_chunk_id,
time_dim->fd.id,
LockTupleExclusive,
RowShareLock);

if (!slice)
{
ereport(ERROR,
errcode(ERRCODE_INTERNAL_ERROR),
errmsg("could not find time dimension slice for chunk %d", osm_chunk_id));
}

ts_cache_release(&hcache);

PG_RETURN_VOID();
}

TSDLLEXPORT bool
ts_hypertable_has_continuous_aggregates(int32 hypertable_id)
{
Expand Down
12 changes: 12 additions & 0 deletions tsl/test/expected/chunk_utils_internal.out
Original file line number Diff line number Diff line change
Expand Up @@ -498,11 +498,18 @@ SELECT status FROM _timescaledb_catalog.hypertable WHERE table_name = 'ht_try';
3

-- must also update the range since the created chunk contains data
BEGIN;
SELECT _timescaledb_functions.lock_osm_chunk_dimension_slice('ht_try');
lock_osm_chunk_dimension_slice
--------------------------------


SELECT _timescaledb_functions.hypertable_osm_range_update('ht_try', '2020-01-01'::timestamptz, '2020-01-02');
hypertable_osm_range_update
-----------------------------
f

COMMIT;
-- OSM chunk is not visible in chunks view
SELECT chunk_name, range_start, range_end
FROM timescaledb_information.chunks
Expand Down Expand Up @@ -659,6 +666,11 @@ SELECT * FROM hypertable_approximate_size('ht_try');
-----------------------------
32768

\set ON_ERROR_STOP 0
-- Error for a hypertable that has no OSM chunk
SELECT _timescaledb_functions.lock_osm_chunk_dimension_slice('test1.hyper1');
ERROR: no OSM chunk found for hypertable test1.hyper1
\set ON_ERROR_STOP 1
--TEST GUC variable to enable/disable OSM chunk
SET timescaledb.enable_tiered_reads=false;
:EXPLAIN SELECT * from ht_try;
Expand Down
71 changes: 37 additions & 34 deletions tsl/test/isolation/expected/osm_range_updates_iso.out
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
Parsed test spec with 10 sessions
Parsed test spec with 11 sessions

starting permutation: LockDimSliceTuple LockDimSliceTuple2 UnlockDimSliceTuple UnlockDimSliceTuple2
step LockDimSliceTuple:
BEGIN;
SELECT _timescaledb_functions.lock_osm_chunk_dimension_slice('osm_test');

lock_osm_chunk_dimension_slice
------------------------------


step LockDimSliceTuple2:
BEGIN;
SELECT _timescaledb_functions.lock_osm_chunk_dimension_slice('osm_test');
<waiting ...>
step UnlockDimSliceTuple: ROLLBACK;
step LockDimSliceTuple2: <... completed>
lock_osm_chunk_dimension_slice
------------------------------


step UnlockDimSliceTuple2: ROLLBACK;

starting permutation: LockDimSliceTuple UR1b UR1u UR2b UR2u UnlockDimSliceTuple UR1c UR2c
step LockDimSliceTuple:
BEGIN;
SELECT range_start, range_end FROM _timescaledb_catalog.dimension_slice
WHERE id IN ( SELECT ds.id FROM
_timescaledb_catalog.chunk ch,
_timescaledb_catalog.dimension_slice ds, _timescaledb_catalog.hypertable ht
WHERE ht.table_name like 'osm_test' AND ds.chunk_id = ch.id AND ht.id = ch.hypertable_id
AND ch.osm_chunk = true
) FOR UPDATE;

range_start| range_end
-------------------+-------------------
9223372036854775806|9223372036854775807
SELECT _timescaledb_functions.lock_osm_chunk_dimension_slice('osm_test');

lock_osm_chunk_dimension_slice
------------------------------


step UR1b: BEGIN;
step UR1u: SELECT _timescaledb_functions.hypertable_osm_range_update('osm_test', 0, 10); <waiting ...>
Expand All @@ -36,17 +51,11 @@ step UR2c: COMMIT;
starting permutation: LockDimSliceTuple DTb UR1b DropOsmChunk UR1u UnlockDimSliceTuple DTc UR1c
step LockDimSliceTuple:
BEGIN;
SELECT range_start, range_end FROM _timescaledb_catalog.dimension_slice
WHERE id IN ( SELECT ds.id FROM
_timescaledb_catalog.chunk ch,
_timescaledb_catalog.dimension_slice ds, _timescaledb_catalog.hypertable ht
WHERE ht.table_name like 'osm_test' AND ds.chunk_id = ch.id AND ht.id = ch.hypertable_id
AND ch.osm_chunk = true
) FOR UPDATE;

range_start| range_end
-------------------+-------------------
9223372036854775806|9223372036854775807
SELECT _timescaledb_functions.lock_osm_chunk_dimension_slice('osm_test');

lock_osm_chunk_dimension_slice
------------------------------


step DTb: BEGIN;
step UR1b: BEGIN;
Expand All @@ -73,17 +82,11 @@ step UR1c: COMMIT;
starting permutation: LockDimSliceTuple DTb UR1b UR1u DropOsmChunk UnlockDimSliceTuple UR1c DTc
step LockDimSliceTuple:
BEGIN;
SELECT range_start, range_end FROM _timescaledb_catalog.dimension_slice
WHERE id IN ( SELECT ds.id FROM
_timescaledb_catalog.chunk ch,
_timescaledb_catalog.dimension_slice ds, _timescaledb_catalog.hypertable ht
WHERE ht.table_name like 'osm_test' AND ds.chunk_id = ch.id AND ht.id = ch.hypertable_id
AND ch.osm_chunk = true
) FOR UPDATE;

range_start| range_end
-------------------+-------------------
9223372036854775806|9223372036854775807
SELECT _timescaledb_functions.lock_osm_chunk_dimension_slice('osm_test');

lock_osm_chunk_dimension_slice
------------------------------


step DTb: BEGIN;
step UR1b: BEGIN;
Expand Down
20 changes: 13 additions & 7 deletions tsl/test/isolation/specs/osm_range_updates_iso.spec
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,18 @@ step "UR2c" { COMMIT; }
session "LDST"
step "LockDimSliceTuple" {
BEGIN;
SELECT range_start, range_end FROM _timescaledb_catalog.dimension_slice
WHERE id IN ( SELECT ds.id FROM
_timescaledb_catalog.chunk ch,
_timescaledb_catalog.dimension_slice ds, _timescaledb_catalog.hypertable ht
WHERE ht.table_name like 'osm_test' AND ds.chunk_id = ch.id AND ht.id = ch.hypertable_id
AND ch.osm_chunk = true
) FOR UPDATE;
SELECT _timescaledb_functions.lock_osm_chunk_dimension_slice('osm_test');
}
step "UnlockDimSliceTuple" { ROLLBACK; }

# second session that locks the same dimension_slice tuple
session "LDST2"
step "LockDimSliceTuple2" {
BEGIN;
SELECT _timescaledb_functions.lock_osm_chunk_dimension_slice('osm_test');
}
step "UnlockDimSliceTuple2" { ROLLBACK; }

session "DT"
step "DTb" { BEGIN; }
step "DropOsmChunk" {
Expand Down Expand Up @@ -102,6 +104,10 @@ step DR2b { BEGIN; }
step DR2drop { SELECT _timescaledb_functions.drop_osm_chunk('test_drop'); }
step DR2c { COMMIT; }

# Two concurrent locks on the same OSM chunk dimension_slice tuple block one
# another. The second lock can only be acquired after the first session rolls
# back and releases its lock.
permutation "LockDimSliceTuple" "LockDimSliceTuple2" "UnlockDimSliceTuple" "UnlockDimSliceTuple2"
# Concurrent updates will block one another
# this previously deadlocked one of the two transactions
permutation "LockDimSliceTuple" "UR1b" "UR1u" "UR2b" "UR2u" "UnlockDimSliceTuple" "UR1c" "UR2c"
Expand Down
1 change: 1 addition & 0 deletions tsl/test/shared/expected/extension.out
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ ORDER BY pronamespace::regnamespace::text COLLATE "C", p.oid::regprocedure::text
_timescaledb_functions.jsonb_get_matching_index_entry(jsonb,text,text)
_timescaledb_functions.last_combinefunc(internal,internal)
_timescaledb_functions.last_sfunc(internal,anyelement,"any")
_timescaledb_functions.lock_osm_chunk_dimension_slice(regclass)
_timescaledb_functions.make_multirange_from_internal_time(tsrange,bigint,bigint)
_timescaledb_functions.make_multirange_from_internal_time(tstzrange,bigint,bigint)
_timescaledb_functions.make_range_from_internal_time(anyrange,anyelement,anyelement)
Expand Down
8 changes: 8 additions & 0 deletions tsl/test/sql/chunk_utils_internal.sql
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,10 @@ SELECT _timescaledb_functions.attach_osm_table_chunk('ht_try', 'child_fdw_table'
-- check hypertable status
SELECT status FROM _timescaledb_catalog.hypertable WHERE table_name = 'ht_try';
-- must also update the range since the created chunk contains data
BEGIN;
SELECT _timescaledb_functions.lock_osm_chunk_dimension_slice('ht_try');
SELECT _timescaledb_functions.hypertable_osm_range_update('ht_try', '2020-01-01'::timestamptz, '2020-01-02');
COMMIT;

-- OSM chunk is not visible in chunks view
SELECT chunk_name, range_start, range_end
Expand Down Expand Up @@ -406,6 +409,11 @@ SELECT _timescaledb_functions.hypertable_osm_range_update('ht_try', '2022-05-05
-- test that approximate size function works when a osm chunk is present
SELECT * FROM hypertable_approximate_size('ht_try');

\set ON_ERROR_STOP 0
-- Error for a hypertable that has no OSM chunk
SELECT _timescaledb_functions.lock_osm_chunk_dimension_slice('test1.hyper1');
\set ON_ERROR_STOP 1

--TEST GUC variable to enable/disable OSM chunk
SET timescaledb.enable_tiered_reads=false;
:EXPLAIN SELECT * from ht_try;
Expand Down
Loading