From be06d65721fcf1595ab3d5c30d13ad38731c044b Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Wed, 17 Aug 2022 11:13:07 +0300 Subject: [PATCH] Nonblocking tenant isolation is supported by using split api. (#6167) --- .../citus_split_shard_by_split_points.c | 11 +- .../distributed/operations/isolate_shards.c | 5 +- .../distributed/operations/repair_shards.c | 3 +- .../distributed/operations/shard_split.c | 27 +- .../distributed/sql/citus--11.0-4--11.1-1.sql | 3 + .../sql/downgrades/citus--11.1-1--11.0-4.sql | 3 + .../isolate_tenant_to_new_shard/11.1-1.sql | 14 + .../isolate_tenant_to_new_shard/8.0-1.sql | 7 + .../isolate_tenant_to_new_shard/latest.sql | 14 + src/include/distributed/repair_shards.h | 1 + src/include/distributed/shard_split.h | 9 +- src/test/regress/enterprise_failure_schedule | 1 + .../regress/enterprise_isolation_schedule | 1 + src/test/regress/enterprise_schedule | 1 + .../expected/alter_distributed_table.out | 2 +- .../expected/citus_local_tables_ent.out | 2 +- .../citus_non_blocking_split_columnar.out | 2 +- .../citus_non_blocking_split_shards.out | 101 ++ ...s_split_shard_by_split_points_negative.out | 2 +- .../expected/failure_tenant_isolation.out | 66 +- .../failure_tenant_isolation_nonblocking.out | 495 +++++++ .../expected/isolation_tenant_isolation.out | 28 +- ...isolation_tenant_isolation_nonblocking.out | 834 +++++++++++ ...enant_isolation_with_fkey_to_reference.out | 10 +- .../regress/expected/multi_create_table.out | 2 +- src/test/regress/expected/multi_extension.out | 4 +- .../multi_multiuser_master_protocol.out | 2 +- .../expected/multi_tenant_isolation.out | 52 +- .../multi_tenant_isolation_nonblocking.out | 1303 +++++++++++++++++ .../expected/single_node_enterprise.out | 2 +- .../expected/upgrade_list_citus_objects.out | 2 +- .../spec/isolation_tenant_isolation.spec | 4 +- ...solation_tenant_isolation_nonblocking.spec | 142 ++ ...nant_isolation_with_fkey_to_reference.spec | 2 +- .../regress/sql/alter_distributed_table.sql | 2 +- .../regress/sql/citus_local_tables_ent.sql | 2 +- .../sql/citus_non_blocking_split_columnar.sql | 2 +- .../sql/citus_non_blocking_split_shards.sql | 47 + ...s_split_shard_by_split_points_negative.sql | 2 +- .../regress/sql/failure_tenant_isolation.sql | 66 +- .../failure_tenant_isolation_nonblocking.sql | 243 +++ src/test/regress/sql/multi_create_table.sql | 2 +- .../sql/multi_multiuser_master_protocol.sql | 2 +- .../regress/sql/multi_tenant_isolation.sql | 52 +- .../multi_tenant_isolation_nonblocking.sql | 637 ++++++++ .../regress/sql/single_node_enterprise.sql | 2 +- 46 files changed, 4042 insertions(+), 174 deletions(-) create mode 100644 src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/11.1-1.sql create mode 100644 src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/8.0-1.sql create mode 100644 src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/latest.sql create mode 100644 src/test/regress/expected/failure_tenant_isolation_nonblocking.out create mode 100644 src/test/regress/expected/isolation_tenant_isolation_nonblocking.out create mode 100644 src/test/regress/expected/multi_tenant_isolation_nonblocking.out create mode 100644 src/test/regress/spec/isolation_tenant_isolation_nonblocking.spec create mode 100644 src/test/regress/sql/failure_tenant_isolation_nonblocking.sql create mode 100644 src/test/regress/sql/multi_tenant_isolation_nonblocking.sql diff --git a/src/backend/distributed/operations/citus_split_shard_by_split_points.c b/src/backend/distributed/operations/citus_split_shard_by_split_points.c index 9cfb80e18..28093f336 100644 --- a/src/backend/distributed/operations/citus_split_shard_by_split_points.c +++ b/src/backend/distributed/operations/citus_split_shard_by_split_points.c @@ -27,8 +27,6 @@ /* declarations for dynamic loading */ PG_FUNCTION_INFO_V1(citus_split_shard_by_split_points); -static SplitMode LookupSplitMode(Oid shardTransferModeOid); - /* * citus_split_shard_by_split_points(shard_id bigint, split_points text[], node_ids integer[], shard_transfer_mode citus.shard_transfer_mode) * Split source shard into multiple shards using the given split points. @@ -81,13 +79,14 @@ LookupSplitMode(Oid shardTransferModeOid) { shardSplitMode = BLOCKING_SPLIT; } - - /* TODO(saawaek): Handle this appropriately based on replica identity */ - else if (strncmp(enumLabel, "auto", NAMEDATALEN) == 0 || - strncmp(enumLabel, "force_logical", NAMEDATALEN) == 0) + else if (strncmp(enumLabel, "force_logical", NAMEDATALEN) == 0) { shardSplitMode = NON_BLOCKING_SPLIT; } + else if (strncmp(enumLabel, "auto", NAMEDATALEN) == 0) + { + shardSplitMode = AUTO_SPLIT; + } else { /* We will not get here as postgres will validate the enum value. */ diff --git a/src/backend/distributed/operations/isolate_shards.c b/src/backend/distributed/operations/isolate_shards.c index 944f91af2..d37ff3bee 100644 --- a/src/backend/distributed/operations/isolate_shards.c +++ b/src/backend/distributed/operations/isolate_shards.c @@ -41,7 +41,6 @@ #include "utils/lsyscache.h" #include "utils/typcache.h" - /* declarations for dynamic loading */ PG_FUNCTION_INFO_V1(isolate_tenant_to_new_shard); PG_FUNCTION_INFO_V1(worker_hash); @@ -60,6 +59,7 @@ isolate_tenant_to_new_shard(PG_FUNCTION_ARGS) Oid relationId = PG_GETARG_OID(0); Datum inputDatum = PG_GETARG_DATUM(1); text *cascadeOptionText = PG_GETARG_TEXT_P(2); + Oid shardTransferModeOid = PG_GETARG_OID(3); EnsureTableOwner(relationId); @@ -163,7 +163,8 @@ isolate_tenant_to_new_shard(PG_FUNCTION_ARGS) nodeIdsForPlacementList = lappend_int(nodeIdsForPlacementList, sourceNodeId); } - SplitShard(BLOCKING_SPLIT, + SplitMode splitMode = LookupSplitMode(shardTransferModeOid); + SplitShard(splitMode, ISOLATE_TENANT_TO_NEW_SHARD, sourceShard->shardId, shardSplitPointsList, diff --git a/src/backend/distributed/operations/repair_shards.c b/src/backend/distributed/operations/repair_shards.c index d13a28e2c..1903e2a85 100644 --- a/src/backend/distributed/operations/repair_shards.c +++ b/src/backend/distributed/operations/repair_shards.c @@ -70,7 +70,6 @@ typedef struct ShardCommandList } ShardCommandList; /* local function forward declarations */ -static void VerifyTablesHaveReplicaIdentity(List *colocatedTableList); static bool RelationCanPublishAllModifications(Oid relationId); static bool CanUseLogicalReplication(Oid relationId, char shardReplicationMode); static void ErrorIfTableCannotBeReplicated(Oid relationId); @@ -591,7 +590,7 @@ ErrorIfMoveUnsupportedTableType(Oid relationId) * do not have a replica identity, which is required for logical replication * to replicate UPDATE and DELETE commands. */ -static void +void VerifyTablesHaveReplicaIdentity(List *colocatedTableList) { ListCell *colocatedTableCell = NULL; diff --git a/src/backend/distributed/operations/shard_split.c b/src/backend/distributed/operations/shard_split.c index f446bbadd..a8fb01655 100644 --- a/src/backend/distributed/operations/shard_split.c +++ b/src/backend/distributed/operations/shard_split.c @@ -29,6 +29,7 @@ #include "distributed/remote_commands.h" #include "distributed/shard_split.h" #include "distributed/reference_table_utils.h" +#include "distributed/repair_shards.h" #include "distributed/multi_partitioning_utils.h" #include "distributed/worker_manager.h" #include "distributed/worker_transaction.h" @@ -64,6 +65,8 @@ typedef struct GroupedDummyShards } GroupedDummyShards; /* Function declarations */ +static void ErrorIfCannotSplitShard(SplitOperation splitOperation, + ShardInterval *sourceShard); static void ErrorIfCannotSplitShardExtended(SplitOperation splitOperation, ShardInterval *shardIntervalToSplit, List *shardSplitPointsList, @@ -136,7 +139,6 @@ static void DropDummyShards(HTAB *mapOfDummyShardToPlacement); static void DropDummyShard(MultiConnection *connection, ShardInterval *shardInterval); static uint64 GetNextShardIdForSplitChild(void); - /* Customize error message strings based on operation type */ static const char *const SplitOperationName[] = { @@ -155,7 +157,7 @@ static const char *const SplitTargetName[] = * ErrorIfCannotSplitShard checks relation kind and invalid shards. It errors * out if we are not able to split the given shard. */ -void +static void ErrorIfCannotSplitShard(SplitOperation splitOperation, ShardInterval *sourceShard) { Oid relationId = sourceShard->relationId; @@ -372,10 +374,22 @@ SplitShard(SplitMode splitMode, SplitOperationName[splitOperation], SplitTargetName[splitOperation]))); } + else if (PlacementMovedUsingLogicalReplicationInTX) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("multiple shard movements/splits via logical " + "replication in the same transaction is currently " + "not supported"))); + } ShardInterval *shardIntervalToSplit = LoadShardInterval(shardIdToSplit); List *colocatedTableList = ColocatedTableList(shardIntervalToSplit->relationId); + if (splitMode == AUTO_SPLIT) + { + VerifyTablesHaveReplicaIdentity(colocatedTableList); + } + Oid relationId = RelationIdForShard(shardIdToSplit); AcquirePlacementColocationLock(relationId, ExclusiveLock, "split"); @@ -433,6 +447,8 @@ SplitShard(SplitMode splitMode, shardIntervalToSplit, shardSplitPointsList, workersForPlacementList); + + PlacementMovedUsingLogicalReplicationInTX = true; } } @@ -1385,10 +1401,6 @@ NonBlockingShardSplit(SplitOperation splitOperation, shardGroupSplitIntervalListList, workersForPlacementList, snapshot); - - /* Used for testing */ - ConflictOnlyWithIsolationTesting(); - /* * 9) Create replica identities, this needs to be done before enabling * the subscriptions. @@ -1414,6 +1426,9 @@ NonBlockingShardSplit(SplitOperation splitOperation, /* 14) Wait for subscribers to catchup till source LSN */ WaitForAllSubscriptionsToCatchUp(sourceConnection, groupedLogicalRepTargetsHash); + /* Used for testing */ + ConflictOnlyWithIsolationTesting(); + /* 15) Block writes on source shards */ BlockWritesToShardList(sourceColocatedShardIntervalList); diff --git a/src/backend/distributed/sql/citus--11.0-4--11.1-1.sql b/src/backend/distributed/sql/citus--11.0-4--11.1-1.sql index 80c40238e..4e58bdd51 100644 --- a/src/backend/distributed/sql/citus--11.0-4--11.1-1.sql +++ b/src/backend/distributed/sql/citus--11.0-4--11.1-1.sql @@ -73,3 +73,6 @@ DROP FUNCTION pg_catalog.get_all_active_transactions(OUT datid oid, OUT process_ #include "udfs/worker_split_shard_replication_setup/11.1-1.sql" #include "udfs/citus_isolation_test_session_is_blocked/11.1-1.sql" #include "udfs/replicate_reference_tables/11.1-1.sql" + +DROP FUNCTION pg_catalog.isolate_tenant_to_new_shard(table_name regclass, tenant_id "any", cascade_option text); +#include "udfs/isolate_tenant_to_new_shard/11.1-1.sql" diff --git a/src/backend/distributed/sql/downgrades/citus--11.1-1--11.0-4.sql b/src/backend/distributed/sql/downgrades/citus--11.1-1--11.0-4.sql index be213c26e..677234c46 100644 --- a/src/backend/distributed/sql/downgrades/citus--11.1-1--11.0-4.sql +++ b/src/backend/distributed/sql/downgrades/citus--11.1-1--11.0-4.sql @@ -92,3 +92,6 @@ DROP FUNCTION pg_catalog.citus_locks(); DROP FUNCTION pg_catalog.replicate_reference_tables(citus.shard_transfer_mode); #include "../udfs/replicate_reference_tables/9.3-2.sql" + +DROP FUNCTION pg_catalog.isolate_tenant_to_new_shard(table_name regclass, tenant_id "any", cascade_option text, shard_transfer_mode citus.shard_transfer_mode); +#include "../udfs/isolate_tenant_to_new_shard/8.0-1.sql" diff --git a/src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/11.1-1.sql b/src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/11.1-1.sql new file mode 100644 index 000000000..d30c875c9 --- /dev/null +++ b/src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/11.1-1.sql @@ -0,0 +1,14 @@ +CREATE FUNCTION pg_catalog.isolate_tenant_to_new_shard( + table_name regclass, + tenant_id "any", + cascade_option text DEFAULT '', + shard_transfer_mode citus.shard_transfer_mode DEFAULT 'auto') +RETURNS bigint LANGUAGE C STRICT +AS 'MODULE_PATHNAME', $$isolate_tenant_to_new_shard$$; + +COMMENT ON FUNCTION pg_catalog.isolate_tenant_to_new_shard( + table_name regclass, + tenant_id "any", + cascade_option text, + shard_transfer_mode citus.shard_transfer_mode) +IS 'isolate a tenant to its own shard and return the new shard id'; diff --git a/src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/8.0-1.sql b/src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/8.0-1.sql new file mode 100644 index 000000000..98e60328a --- /dev/null +++ b/src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/8.0-1.sql @@ -0,0 +1,7 @@ +CREATE FUNCTION pg_catalog.isolate_tenant_to_new_shard(table_name regclass, tenant_id "any", cascade_option text DEFAULT '') + RETURNS bigint + LANGUAGE C STRICT + AS 'MODULE_PATHNAME', $$isolate_tenant_to_new_shard$$; + +COMMENT ON FUNCTION pg_catalog.isolate_tenant_to_new_shard(table_name regclass, tenant_id "any", cascade_option text) + IS 'isolate a tenant to its own shard and return the new shard id'; diff --git a/src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/latest.sql b/src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/latest.sql new file mode 100644 index 000000000..d30c875c9 --- /dev/null +++ b/src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/latest.sql @@ -0,0 +1,14 @@ +CREATE FUNCTION pg_catalog.isolate_tenant_to_new_shard( + table_name regclass, + tenant_id "any", + cascade_option text DEFAULT '', + shard_transfer_mode citus.shard_transfer_mode DEFAULT 'auto') +RETURNS bigint LANGUAGE C STRICT +AS 'MODULE_PATHNAME', $$isolate_tenant_to_new_shard$$; + +COMMENT ON FUNCTION pg_catalog.isolate_tenant_to_new_shard( + table_name regclass, + tenant_id "any", + cascade_option text, + shard_transfer_mode citus.shard_transfer_mode) +IS 'isolate a tenant to its own shard and return the new shard id'; diff --git a/src/include/distributed/repair_shards.h b/src/include/distributed/repair_shards.h index 6a209a3b4..fa0d76190 100644 --- a/src/include/distributed/repair_shards.h +++ b/src/include/distributed/repair_shards.h @@ -16,3 +16,4 @@ extern uint64 ShardListSizeInBytes(List *colocatedShardList, extern void ErrorIfMoveUnsupportedTableType(Oid relationId); extern void CopyShardsToNode(WorkerNode *sourceNode, WorkerNode *targetNode, List *shardIntervalList, char *snapshotName); +extern void VerifyTablesHaveReplicaIdentity(List *colocatedTableList); diff --git a/src/include/distributed/shard_split.h b/src/include/distributed/shard_split.h index 11aeb6c00..7464534b7 100644 --- a/src/include/distributed/shard_split.h +++ b/src/include/distributed/shard_split.h @@ -16,9 +16,11 @@ typedef enum SplitMode { BLOCKING_SPLIT = 0, - NON_BLOCKING_SPLIT = 1 + NON_BLOCKING_SPLIT = 1, + AUTO_SPLIT = 2 } SplitMode; + /* * User Scenario calling Split Shard API. * The 'SplitOperation' type is used to customize info/error messages based on user scenario. @@ -40,9 +42,8 @@ extern void SplitShard(SplitMode splitMode, List *shardSplitPointsList, List *nodeIdsForPlacementList); -/* TODO(niupre): Make all these APIs private when all consumers (Example : ISOLATE_TENANT_TO_NEW_SHARD) directly call 'SplitShard' API. */ -extern void ErrorIfCannotSplitShard(SplitOperation splitOperation, - ShardInterval *sourceShard); extern void DropShardList(List *shardIntervalList); +extern SplitMode LookupSplitMode(Oid shardTransferModeOid); + #endif /* SHARDSPLIT_H_ */ diff --git a/src/test/regress/enterprise_failure_schedule b/src/test/regress/enterprise_failure_schedule index 253134142..cc7b4f2d4 100644 --- a/src/test/regress/enterprise_failure_schedule +++ b/src/test/regress/enterprise_failure_schedule @@ -9,4 +9,5 @@ test: failure_online_move_shard_placement test: failure_on_create_subscription test: failure_offline_move_shard_placement test: failure_tenant_isolation +test: failure_tenant_isolation_nonblocking test: check_mx diff --git a/src/test/regress/enterprise_isolation_schedule b/src/test/regress/enterprise_isolation_schedule index 77134c3b2..3e6655f88 100644 --- a/src/test/regress/enterprise_isolation_schedule +++ b/src/test/regress/enterprise_isolation_schedule @@ -13,5 +13,6 @@ test: isolation_ref2ref_foreign_keys_enterprise test: isolation_pg_send_cancellation test: isolation_shard_move_vs_start_metadata_sync test: isolation_tenant_isolation +test: isolation_tenant_isolation_nonblocking test: isolation_blocking_shard_split test: isolation_blocking_shard_split_with_fkey_to_reference diff --git a/src/test/regress/enterprise_schedule b/src/test/regress/enterprise_schedule index 3acbc3d26..84341d23d 100644 --- a/src/test/regress/enterprise_schedule +++ b/src/test/regress/enterprise_schedule @@ -7,6 +7,7 @@ test: multi_cluster_management test: multi_test_catalog_views test: multi_data_types test: multi_tenant_isolation +test: multi_tenant_isolation_nonblocking # -------- # Tests that require adding coordinator to pg_dist_node diff --git a/src/test/regress/expected/alter_distributed_table.out b/src/test/regress/expected/alter_distributed_table.out index 0fb80a3fc..b29b10168 100644 --- a/src/test/regress/expected/alter_distributed_table.out +++ b/src/test/regress/expected/alter_distributed_table.out @@ -635,7 +635,7 @@ SELECT create_distributed_table ('shard_split_table', 'a'); (1 row) -SELECT 1 FROM isolate_tenant_to_new_shard('shard_split_table', 5); +SELECT 1 FROM isolate_tenant_to_new_shard('shard_split_table', 5, shard_transfer_mode => 'block_writes'); ?column? --------------------------------------------------------------------- 1 diff --git a/src/test/regress/expected/citus_local_tables_ent.out b/src/test/regress/expected/citus_local_tables_ent.out index 3d67e44a9..21928eefd 100644 --- a/src/test/regress/expected/citus_local_tables_ent.out +++ b/src/test/regress/expected/citus_local_tables_ent.out @@ -22,7 +22,7 @@ SELECT citus_add_local_table_to_metadata('citus_local_table'); (1 row) -- isolate_tenant_to_new_shard is not supported -SELECT isolate_tenant_to_new_shard('citus_local_table', 100); +SELECT isolate_tenant_to_new_shard('citus_local_table', 100, shard_transfer_mode => 'block_writes'); ERROR: cannot isolate tenant because tenant isolation is only support for hash distributed tables -- master_copy_shard_placement is not supported SELECT master_copy_shard_placement(shardid, 'localhost', :master_port, 'localhost', :worker_1_port, false) diff --git a/src/test/regress/expected/citus_non_blocking_split_columnar.out b/src/test/regress/expected/citus_non_blocking_split_columnar.out index c54b3f827..e8365ec01 100644 --- a/src/test/regress/expected/citus_non_blocking_split_columnar.out +++ b/src/test/regress/expected/citus_non_blocking_split_columnar.out @@ -505,7 +505,7 @@ SET citus.shard_replication_factor TO 1; 8999002, -- sensors_old ARRAY['-2127770000'], ARRAY[:worker_1_node, :worker_2_node], - 'auto'); + 'force_logical'); citus_split_shard_by_split_points --------------------------------------------------------------------- diff --git a/src/test/regress/expected/citus_non_blocking_split_shards.out b/src/test/regress/expected/citus_non_blocking_split_shards.out index 7a3efbc7b..881704280 100644 --- a/src/test/regress/expected/citus_non_blocking_split_shards.out +++ b/src/test/regress/expected/citus_non_blocking_split_shards.out @@ -9,6 +9,10 @@ Here is a high level overview of test plan: 6. Trigger Split on both shards of 'sensors'. This will also split co-located tables. 7. Move one of the split shard to test Split -> ShardMove. 8. Split an already split shard second time on a different schema. + 9. Create a colocated table with no replica identity. + 10. Show we do not allow Split with the shard transfer mode 'auto' if any colocated table has no replica identity. + 11. Drop the colocated table with no replica identity. + 12. Show we allow Split with the shard transfer mode 'auto' if all colocated tables has replica identity. */ CREATE SCHEMA "citus_split_test_schema"; CREATE ROLE test_shard_split_role WITH LOGIN; @@ -428,6 +432,103 @@ SELECT shard.shardid, logicalrelid, shardminvalue, shardmaxvalue, nodename, node (18 rows) -- END: Split second time on another schema +-- BEGIN: Create a co-located table with no replica identity. +CREATE TABLE table_no_rep_id (measureid integer); +SELECT create_distributed_table('table_no_rep_id', 'measureid', colocate_with:='sensors'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- END: Create a co-located table with no replica identity. +-- BEGIN: Split a shard with shard_transfer_mode='auto' and with a colocated table with no replica identity +SET citus.next_shard_id TO 8981041; +SELECT pg_catalog.citus_split_shard_by_split_points( + 8981031, + ARRAY['-2120000000'], + ARRAY[:worker_1_node, :worker_2_node], + 'auto'); +ERROR: cannot use logical replication to transfer shards of the relation table_no_rep_id since it doesn't have a REPLICA IDENTITY or PRIMARY KEY +DETAIL: UPDATE and DELETE commands on the shard will error out during logical replication unless there is a REPLICA IDENTITY or PRIMARY KEY. +HINT: If you wish to continue without a replica identity set the shard_transfer_mode to 'force_logical' or 'block_writes'. +SELECT shard.shardid, logicalrelid, shardminvalue, shardmaxvalue, nodename, nodeport + FROM pg_dist_shard AS shard + INNER JOIN pg_dist_placement placement ON shard.shardid = placement.shardid + INNER JOIN pg_dist_node node ON placement.groupid = node.groupid + INNER JOIN pg_catalog.pg_class cls ON shard.logicalrelid = cls.oid + WHERE node.noderole = 'primary' AND (logicalrelid = 'sensors'::regclass OR logicalrelid = 'colocated_dist_table'::regclass OR logicalrelid = 'table_with_index_rep_identity'::regclass) + ORDER BY logicalrelid, shardminvalue::BIGINT; + shardid | logicalrelid | shardminvalue | shardmaxvalue | nodename | nodeport +--------------------------------------------------------------------- + 8981031 | sensors | -2147483648 | -2100000000 | localhost | 57637 + 8981032 | sensors | -2099999999 | -1073741824 | localhost | 57638 + 8981008 | sensors | -1073741823 | -1 | localhost | 57638 + 8981013 | sensors | 0 | 536870911 | localhost | 57637 + 8981014 | sensors | 536870912 | 1610612735 | localhost | 57637 + 8981015 | sensors | 1610612736 | 2147483647 | localhost | 57638 + 8981033 | colocated_dist_table | -2147483648 | -2100000000 | localhost | 57637 + 8981034 | colocated_dist_table | -2099999999 | -1073741824 | localhost | 57638 + 8981010 | colocated_dist_table | -1073741823 | -1 | localhost | 57638 + 8981016 | colocated_dist_table | 0 | 536870911 | localhost | 57637 + 8981017 | colocated_dist_table | 536870912 | 1610612735 | localhost | 57637 + 8981018 | colocated_dist_table | 1610612736 | 2147483647 | localhost | 57638 + 8981035 | table_with_index_rep_identity | -2147483648 | -2100000000 | localhost | 57637 + 8981036 | table_with_index_rep_identity | -2099999999 | -1073741824 | localhost | 57638 + 8981012 | table_with_index_rep_identity | -1073741823 | -1 | localhost | 57638 + 8981019 | table_with_index_rep_identity | 0 | 536870911 | localhost | 57637 + 8981020 | table_with_index_rep_identity | 536870912 | 1610612735 | localhost | 57637 + 8981021 | table_with_index_rep_identity | 1610612736 | 2147483647 | localhost | 57638 +(18 rows) + +-- END: Split a shard with shard_transfer_mode='auto' and with a colocated table with no replica identity +-- BEGIN: Drop the co-located table with no replica identity. +DROP TABLE table_no_rep_id; +-- END: Drop the co-located table with no replica identity. +-- BEGIN: Split a shard with shard_transfer_mode='auto' and with all colocated tables has replica identity +SET citus.next_shard_id TO 8981041; +SELECT pg_catalog.citus_split_shard_by_split_points( + 8981031, + ARRAY['-2120000000'], + ARRAY[:worker_1_node, :worker_2_node], + 'auto'); + citus_split_shard_by_split_points +--------------------------------------------------------------------- + +(1 row) + +SELECT shard.shardid, logicalrelid, shardminvalue, shardmaxvalue, nodename, nodeport + FROM pg_dist_shard AS shard + INNER JOIN pg_dist_placement placement ON shard.shardid = placement.shardid + INNER JOIN pg_dist_node node ON placement.groupid = node.groupid + INNER JOIN pg_catalog.pg_class cls ON shard.logicalrelid = cls.oid + WHERE node.noderole = 'primary' AND (logicalrelid = 'sensors'::regclass OR logicalrelid = 'colocated_dist_table'::regclass OR logicalrelid = 'table_with_index_rep_identity'::regclass) + ORDER BY logicalrelid, shardminvalue::BIGINT; + shardid | logicalrelid | shardminvalue | shardmaxvalue | nodename | nodeport +--------------------------------------------------------------------- + 8981041 | sensors | -2147483648 | -2120000000 | localhost | 57637 + 8981042 | sensors | -2119999999 | -2100000000 | localhost | 57638 + 8981032 | sensors | -2099999999 | -1073741824 | localhost | 57638 + 8981008 | sensors | -1073741823 | -1 | localhost | 57638 + 8981013 | sensors | 0 | 536870911 | localhost | 57637 + 8981014 | sensors | 536870912 | 1610612735 | localhost | 57637 + 8981015 | sensors | 1610612736 | 2147483647 | localhost | 57638 + 8981043 | colocated_dist_table | -2147483648 | -2120000000 | localhost | 57637 + 8981044 | colocated_dist_table | -2119999999 | -2100000000 | localhost | 57638 + 8981034 | colocated_dist_table | -2099999999 | -1073741824 | localhost | 57638 + 8981010 | colocated_dist_table | -1073741823 | -1 | localhost | 57638 + 8981016 | colocated_dist_table | 0 | 536870911 | localhost | 57637 + 8981017 | colocated_dist_table | 536870912 | 1610612735 | localhost | 57637 + 8981018 | colocated_dist_table | 1610612736 | 2147483647 | localhost | 57638 + 8981045 | table_with_index_rep_identity | -2147483648 | -2120000000 | localhost | 57637 + 8981046 | table_with_index_rep_identity | -2119999999 | -2100000000 | localhost | 57638 + 8981036 | table_with_index_rep_identity | -2099999999 | -1073741824 | localhost | 57638 + 8981012 | table_with_index_rep_identity | -1073741823 | -1 | localhost | 57638 + 8981019 | table_with_index_rep_identity | 0 | 536870911 | localhost | 57637 + 8981020 | table_with_index_rep_identity | 536870912 | 1610612735 | localhost | 57637 + 8981021 | table_with_index_rep_identity | 1610612736 | 2147483647 | localhost | 57638 +(21 rows) + +-- END: Split a shard with shard_transfer_mode='auto' and with all colocated tables has replica identity -- BEGIN: Validate Data Count SELECT COUNT(*) FROM sensors; count diff --git a/src/test/regress/expected/citus_split_shard_by_split_points_negative.out b/src/test/regress/expected/citus_split_shard_by_split_points_negative.out index 1bb3fb12d..6fa2559a1 100644 --- a/src/test/regress/expected/citus_split_shard_by_split_points_negative.out +++ b/src/test/regress/expected/citus_split_shard_by_split_points_negative.out @@ -101,7 +101,7 @@ FROM shard_ranges; ERROR: Resulting shard count '64001' with split is greater than max shard count '64000' limit. -- UDF fails where source shard cannot be split further i.e min and max range is equal. -- Create a Shard where range cannot be split further -SELECT isolate_tenant_to_new_shard('table_to_split', 1); +SELECT isolate_tenant_to_new_shard('table_to_split', 1, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- 49761305 diff --git a/src/test/regress/expected/failure_tenant_isolation.out b/src/test/regress/expected/failure_tenant_isolation.out index 690b19672..6b7fde064 100644 --- a/src/test/regress/expected/failure_tenant_isolation.out +++ b/src/test/regress/expected/failure_tenant_isolation.out @@ -56,7 +56,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_ (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: connection not open CONTEXT: while executing command on localhost:xxxxx -- cancellation on colocated table creation @@ -66,7 +66,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_ (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on colocated table population SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(302").kill()'); @@ -75,7 +75,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(302").kill()'); (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open -- cancellation on colocated table population SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(302").cancel(' || :pid || ')'); @@ -84,7 +84,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(302").cancel(' || (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on colocated table constraints SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_2 ADD CONSTRAINT").after(1).kill()'); @@ -93,7 +93,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_2 (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open -- cancellation on colocated table constraints SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_2 ADD CONSTRAINT").after(2).cancel(' || :pid || ')'); @@ -102,7 +102,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_2 (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on table creation SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_1").kill()'); @@ -111,7 +111,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_ (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: connection not open CONTEXT: while executing command on localhost:xxxxx -- cancellation on table creation @@ -121,7 +121,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_ (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on table population SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(300").kill()'); @@ -130,7 +130,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(300").kill()'); (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open -- cancellation on table population SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(300").cancel(' || :pid || ')'); @@ -139,7 +139,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(300").cancel(' || (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on table constraints SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_1 ADD CONSTRAINT").after(1).kill()'); @@ -148,7 +148,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_1 (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open -- cancellation on table constraints SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_1 ADD CONSTRAINT").after(2).cancel(' || :pid || ')'); @@ -157,7 +157,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_1 (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on dropping old colocated shard SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_2").kill()'); @@ -166,7 +166,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolatio (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: connection not open CONTEXT: while executing command on localhost:xxxxx -- cancellation on dropping old colocated shard @@ -176,7 +176,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolatio (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on dropping old shard SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_1").kill()'); @@ -185,7 +185,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolatio (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: connection not open CONTEXT: while executing command on localhost:xxxxx -- cancellation on dropping old shard @@ -195,7 +195,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolatio (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on foreign key creation SELECT citus.mitmproxy('conn.onQuery(query="ADD CONSTRAINT table_2_ref_id_fkey FOREIGN KEY").kill()'); @@ -204,7 +204,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="ADD CONSTRAINT table_2_ref_id_fkey F (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: connection not open CONTEXT: while executing command on localhost:xxxxx -- failure on foreign key creation @@ -214,7 +214,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="ADD CONSTRAINT table_2_ref_id_fkey F (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on shard split transaction SELECT citus.mitmproxy('conn.onQuery(query="BEGIN").kill()'); @@ -223,7 +223,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="BEGIN").kill()'); (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: failure on connection marked as essential: localhost:xxxxx -- failure on shard split transaction SELECT citus.mitmproxy('conn.onQuery(query="BEGIN").cancel(' || :pid || ')'); @@ -232,7 +232,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="BEGIN").cancel(' || :pid || ')'); (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on shard split transaction commit SELECT citus.mitmproxy('conn.onQuery(query="COMMIT").kill()'); @@ -241,7 +241,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="COMMIT").kill()'); (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: failure on connection marked as essential: localhost:xxxxx -- failure on shard split transaction commit SELECT citus.mitmproxy('conn.onQuery(query="COMMIT").cancel(' || :pid || ')'); @@ -250,7 +250,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="COMMIT").cancel(' || :pid || ')'); (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on transaction for dropping old tables SELECT citus.mitmproxy('conn.after(1).onQuery(query="BEGIN").kill()'); @@ -259,7 +259,7 @@ SELECT citus.mitmproxy('conn.after(1).onQuery(query="BEGIN").kill()'); (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: failure on connection marked as essential: localhost:xxxxx -- failure on transaction for dropping old tables SELECT citus.mitmproxy('conn.after(1).onQuery(query="BEGIN").cancel(' || :pid || ')'); @@ -268,7 +268,7 @@ SELECT citus.mitmproxy('conn.after(1).onQuery(query="BEGIN").cancel(' || :pid || (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on transaction for foreign key creation SELECT citus.mitmproxy('conn.after(2).onQuery(query="BEGIN").kill()'); @@ -277,7 +277,7 @@ SELECT citus.mitmproxy('conn.after(2).onQuery(query="BEGIN").kill()'); (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: failure on connection marked as essential: localhost:xxxxx -- failure on transaction for foreign key creation SELECT citus.mitmproxy('conn.after(2).onQuery(query="BEGIN").cancel(' || :pid || ')'); @@ -286,7 +286,7 @@ SELECT citus.mitmproxy('conn.after(2).onQuery(query="BEGIN").cancel(' || :pid || (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on transaction commit for foreign key creation SELECT citus.mitmproxy('conn.after(1).onQuery(query="COMMIT").kill()'); @@ -295,7 +295,7 @@ SELECT citus.mitmproxy('conn.after(1).onQuery(query="COMMIT").kill()'); (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: failure on connection marked as essential: localhost:xxxxx -- failure on transaction commit for foreign key creation SELECT citus.mitmproxy('conn.after(1).onQuery(query="COMMIT").cancel(' || :pid || ')'); @@ -304,7 +304,7 @@ SELECT citus.mitmproxy('conn.after(1).onQuery(query="COMMIT").cancel(' || :pid | (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on transaction prepare for dropping old tables SELECT citus.mitmproxy('conn.onQuery(query="PREPARE TRANSACTION").kill()'); @@ -319,7 +319,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="PREPARE TRANSACTION").kill()'); DO LANGUAGE plpgsql $$ BEGIN - SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); + SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); EXCEPTION WHEN OTHERS THEN RAISE 'Command failed to execute'; END; @@ -333,7 +333,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="PREPARE TRANSACTION").cancel(' || :p (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- failure on transaction commit for dropping old tables SELECT citus.mitmproxy('conn.after(2).onQuery(query="COMMIT").kill()'); @@ -342,7 +342,7 @@ SELECT citus.mitmproxy('conn.after(2).onQuery(query="COMMIT").kill()'); (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: failure on connection marked as essential: localhost:xxxxx -- failure on transaction commit for dropping old tables SELECT citus.mitmproxy('conn.after(2).onQuery(query="COMMIT").cancel(' || :pid || ')'); @@ -351,7 +351,7 @@ SELECT citus.mitmproxy('conn.after(2).onQuery(query="COMMIT").cancel(' || :pid | (1 row) -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: canceling statement due to user request -- verify that the tenant is not isolated SELECT * FROM shard_sizes ORDER BY 1; @@ -371,7 +371,7 @@ SELECT citus.mitmproxy('conn.allow()'); -- shard sizes after successful tenant isolation CREATE TABLE old_shards AS SELECT shardid FROM pg_dist_shard; WITH new_shard AS ( - SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE') AS shardid + SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes') AS shardid ) SELECT row_count FROM shard_sizes diff --git a/src/test/regress/expected/failure_tenant_isolation_nonblocking.out b/src/test/regress/expected/failure_tenant_isolation_nonblocking.out new file mode 100644 index 000000000..5af9df776 --- /dev/null +++ b/src/test/regress/expected/failure_tenant_isolation_nonblocking.out @@ -0,0 +1,495 @@ +-- +-- failure_tenant_isolation +-- +-- due to different libpq versions +-- some warning messages differ +-- between local and CI +SET client_min_messages TO ERROR; +CREATE SCHEMA IF NOT EXISTS tenant_isolation; +SET SEARCH_PATH = tenant_isolation; +SET citus.shard_count TO 2; +SET citus.next_shard_id TO 300; +SET citus.shard_replication_factor TO 1; +SET citus.max_adaptive_executor_pool_size TO 1; +SELECT pg_backend_pid() as pid \gset +SELECT citus.mitmproxy('conn.allow()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE table_1 (id int PRIMARY KEY); +CREATE TABLE table_2 (ref_id int REFERENCES table_1(id) UNIQUE, data int); +SELECT create_distributed_table('table_1', 'id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('table_2', 'ref_id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE VIEW shard_sizes AS + SELECT shardid, result AS row_count + FROM run_command_on_placements('table_1', 'SELECT count(*) FROM %s'); +INSERT INTO table_1 +SELECT x +FROM generate_series(1, 100) AS f (x); +INSERT INTO table_2 +SELECT x, x +FROM generate_series(1, 100) AS f (x); +-- initial shard sizes +SELECT * FROM shard_sizes ORDER BY 1; + shardid | row_count +--------------------------------------------------------------------- + 300 | 49 + 301 | 51 +(2 rows) + +-- failure on table creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_1").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection not open +CONTEXT: while executing command on localhost:xxxxx +-- cancellation on table creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_1").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on colocated table creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_2").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection not open +CONTEXT: while executing command on localhost:xxxxx +-- cancellation on colocated table creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_2").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on table constraints on replica identity creation +SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_1 ADD CONSTRAINT").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection not open +CONTEXT: while executing command on localhost:xxxxx +-- cancellation on table constraints on replica identity creation +SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_1 ADD CONSTRAINT").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on publication creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE PUBLICATION").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection not open +CONTEXT: while executing command on localhost:xxxxx +-- cancellation on publication creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE PUBLICATION").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on replication slot creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE_REPLICATION_SLOT").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection not open +CONTEXT: while executing command on localhost:xxxxx +-- cancellation on replication slot creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE_REPLICATION_SLOT").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on setting snapshot +SELECT citus.mitmproxy('conn.onQuery(query="SET TRANSACTION SNAPSHOT").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- cancellation on setting snapshot +SELECT citus.mitmproxy('conn.onQuery(query="SET TRANSACTION SNAPSHOT").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on table population +SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(300").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- cancellation on table population +SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(300").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on colocated table population +SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(302").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- cancellation on colocated table population +SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(302").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on replication setup udf +SELECT citus.mitmproxy('conn.onQuery(query="worker_split_shard_replication_setup").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: Failed to run worker_split_shard_replication_setup UDF. It should successfully execute for splitting a shard in a non-blocking way. Please retry. +-- cancellation on replication setup udf +SELECT citus.mitmproxy('conn.onQuery(query="worker_split_shard_replication_setup").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on subscription creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE SUBSCRIPTION").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection not open +CONTEXT: while executing command on localhost:xxxxx +-- cancellation on subscription creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE SUBSCRIPTION").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on colocated table constraints +SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_2 ADD CONSTRAINT").after(1).kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- cancellation on colocated table constraints +SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_2 ADD CONSTRAINT").after(2).cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on catching up LSN +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_current_wal_lsn").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection not open +CONTEXT: while executing command on localhost:xxxxx +-- cancellation on catching up LSN +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_current_wal_lsn").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on dropping subscription +SELECT citus.mitmproxy('conn.onQuery(query="DROP SUBSCRIPTION").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection not open +CONTEXT: while executing command on localhost:xxxxx +-- cancellation on dropping subscription +SELECT citus.mitmproxy('conn.onQuery(query="DROP SUBSCRIPTION").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on dropping publication +SELECT citus.mitmproxy('conn.onQuery(query="DROP PUBLICATION").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection not open +CONTEXT: while executing command on localhost:xxxxx +-- cancellation on dropping publication +SELECT citus.mitmproxy('conn.onQuery(query="DROP PUBLICATION").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on dropping replication slot +SELECT citus.mitmproxy('conn.onQuery(query="select pg_drop_replication_slot").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection not open +CONTEXT: while executing command on localhost:xxxxx +-- cancellation on dropping replication slot +SELECT citus.mitmproxy('conn.onQuery(query="select pg_drop_replication_slot").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on dropping old shard +SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_1").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection not open +CONTEXT: while executing command on localhost:xxxxx +-- cancellation on dropping old shard +SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_1").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on dropping old colocated shard +SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_2").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection not open +CONTEXT: while executing command on localhost:xxxxx +-- cancellation on dropping old colocated shard +SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_2").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on foreign key creation +SELECT citus.mitmproxy('conn.onQuery(query="ADD CONSTRAINT table_2_ref_id_fkey FOREIGN KEY").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: connection not open +CONTEXT: while executing command on localhost:xxxxx +-- failure on foreign key creation +SELECT citus.mitmproxy('conn.onQuery(query="ADD CONSTRAINT table_2_ref_id_fkey FOREIGN KEY").after(2).cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on shard split transaction +SELECT citus.mitmproxy('conn.onQuery(query="BEGIN").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: failure on connection marked as essential: localhost:xxxxx +-- failure on shard split transaction +SELECT citus.mitmproxy('conn.onQuery(query="BEGIN").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on shard split transaction commit +SELECT citus.mitmproxy('conn.onQuery(query="COMMIT").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: failure on connection marked as essential: localhost:xxxxx +-- failure on shard split transaction commit +SELECT citus.mitmproxy('conn.onQuery(query="COMMIT").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- failure on transaction prepare for dropping old tables +SELECT citus.mitmproxy('conn.onQuery(query="PREPARE TRANSACTION").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +-- due to libpq version differences, the output might change +-- hence use code block to catch the error +\set VERBOSITY terse +DO LANGUAGE plpgsql +$$ +BEGIN + SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + EXCEPTION WHEN OTHERS THEN + RAISE 'Command failed to execute'; +END; +$$; +ERROR: Command failed to execute +\set VERBOSITY default +-- failure on transaction prepare for dropping old tables +SELECT citus.mitmproxy('conn.onQuery(query="PREPARE TRANSACTION").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); +ERROR: canceling statement due to user request +-- verify that the tenant is not isolated +SELECT * FROM shard_sizes ORDER BY 1; + shardid | row_count +--------------------------------------------------------------------- + 300 | 49 + 301 | 51 +(2 rows) + +-- Verify that tenant can be isolated after unsuccessful attempts +SELECT citus.mitmproxy('conn.allow()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +-- shard sizes after successful tenant isolation +CREATE TABLE old_shards AS SELECT shardid FROM pg_dist_shard; +WITH new_shard AS ( + SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical') AS shardid +) +SELECT row_count +FROM shard_sizes +JOIN new_shard ON shard_sizes.shardid = new_shard.shardid; + row_count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT row_count +FROM shard_sizes +WHERE shard_sizes.shardid NOT IN (SELECT * FROM old_shards) +ORDER BY 1; + row_count +--------------------------------------------------------------------- + 1 + 20 + 28 +(3 rows) + +\set VERBOSITY terse +DROP SCHEMA tenant_isolation CASCADE; +\set VERBOSITY default diff --git a/src/test/regress/expected/isolation_tenant_isolation.out b/src/test/regress/expected/isolation_tenant_isolation.out index eba468c13..f663aa05d 100644 --- a/src/test/regress/expected/isolation_tenant_isolation.out +++ b/src/test/regress/expected/isolation_tenant_isolation.out @@ -30,7 +30,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -103,7 +103,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -173,7 +173,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -242,7 +242,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -311,7 +311,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -395,7 +395,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -465,7 +465,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -532,7 +532,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -598,7 +598,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -664,7 +664,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -740,7 +740,7 @@ step s1-begin: SET citus.select_opens_transaction_block TO false; step s1-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 2); + SELECT isolate_tenant_to_new_shard('isolation_table', 2, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -748,7 +748,7 @@ isolate_tenant_to_new_shard (1 row) step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); ERROR: could not acquire the lock required to split public.isolation_table step s1-commit: @@ -795,7 +795,7 @@ step s1-begin: SET citus.select_opens_transaction_block TO false; step s1-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 2); + SELECT isolate_tenant_to_new_shard('isolation_table', 2, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -803,7 +803,7 @@ isolate_tenant_to_new_shard (1 row) step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); ERROR: could not acquire the lock required to split public.isolation_table step s1-commit: diff --git a/src/test/regress/expected/isolation_tenant_isolation_nonblocking.out b/src/test/regress/expected/isolation_tenant_isolation_nonblocking.out new file mode 100644 index 000000000..cd6b9fa22 --- /dev/null +++ b/src/test/regress/expected/isolation_tenant_isolation_nonblocking.out @@ -0,0 +1,834 @@ +Parsed test spec with 3 sessions + +starting permutation: s1-load-cache s1-insert s3-acquire-advisory-lock s1-begin s1-select s2-begin s2-isolate-tenant s1-update s1-commit s3-release-advisory-lock s2-commit s2-print-cluster +create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +step s1-load-cache: + TRUNCATE isolation_table; + +step s1-insert: + INSERT INTO isolation_table VALUES (5, 10); + +step s3-acquire-advisory-lock: + SELECT pg_advisory_lock(44000, 55152); + +pg_advisory_lock +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + -- the tests are written with the logic where single shard SELECTs + -- do not to open transaction blocks + SET citus.select_opens_transaction_block TO false; + +step s1-select: + SELECT count(*) FROM isolation_table WHERE id = 5; + +count +--------------------------------------------------------------------- + 1 +(1 row) + +step s2-begin: + BEGIN; + +step s2-isolate-tenant: + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'force_logical'); + +step s1-update: + UPDATE isolation_table SET value = 5 WHERE id = 5; + +step s1-commit: + COMMIT; + +step s3-release-advisory-lock: + SELECT pg_advisory_unlock(44000, 55152); + +pg_advisory_unlock +--------------------------------------------------------------------- +t +(1 row) + +step s2-isolate-tenant: <... completed> +isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1500076 +(1 row) + +step s2-commit: + COMMIT; + +step s2-print-cluster: + -- row count per shard + SELECT + nodeport, shardid, success, result + FROM + run_command_on_placements('isolation_table', 'select count(*) from %s') + ORDER BY + nodeport, shardid; + -- rows + SELECT id, value FROM isolation_table ORDER BY id, value; + +nodeport|shardid|success|result +--------------------------------------------------------------------- + 57637|1500075|t | 0 + 57637|1500076|t | 1 + 57637|1500077|t | 0 + 57638|1500074|t | 0 +(4 rows) + +id|value +--------------------------------------------------------------------- + 5| 5 +(1 row) + + +starting permutation: s1-load-cache s1-insert s3-acquire-advisory-lock s1-begin s1-select s2-begin s2-isolate-tenant s1-delete s1-commit s3-release-advisory-lock s2-commit s2-print-cluster +create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +step s1-load-cache: + TRUNCATE isolation_table; + +step s1-insert: + INSERT INTO isolation_table VALUES (5, 10); + +step s3-acquire-advisory-lock: + SELECT pg_advisory_lock(44000, 55152); + +pg_advisory_lock +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + -- the tests are written with the logic where single shard SELECTs + -- do not to open transaction blocks + SET citus.select_opens_transaction_block TO false; + +step s1-select: + SELECT count(*) FROM isolation_table WHERE id = 5; + +count +--------------------------------------------------------------------- + 1 +(1 row) + +step s2-begin: + BEGIN; + +step s2-isolate-tenant: + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'force_logical'); + +step s1-delete: + DELETE FROM isolation_table WHERE id = 5; + +step s1-commit: + COMMIT; + +step s3-release-advisory-lock: + SELECT pg_advisory_unlock(44000, 55152); + +pg_advisory_unlock +--------------------------------------------------------------------- +t +(1 row) + +step s2-isolate-tenant: <... completed> +isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1500082 +(1 row) + +step s2-commit: + COMMIT; + +step s2-print-cluster: + -- row count per shard + SELECT + nodeport, shardid, success, result + FROM + run_command_on_placements('isolation_table', 'select count(*) from %s') + ORDER BY + nodeport, shardid; + -- rows + SELECT id, value FROM isolation_table ORDER BY id, value; + +nodeport|shardid|success|result +--------------------------------------------------------------------- + 57637|1500081|t | 0 + 57637|1500082|t | 0 + 57637|1500083|t | 0 + 57638|1500080|t | 0 +(4 rows) + +id|value +--------------------------------------------------------------------- +(0 rows) + + +starting permutation: s1-load-cache s3-acquire-advisory-lock s1-begin s1-select s2-begin s2-isolate-tenant s1-insert s1-commit s3-release-advisory-lock s2-commit s2-print-cluster +create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +step s1-load-cache: + TRUNCATE isolation_table; + +step s3-acquire-advisory-lock: + SELECT pg_advisory_lock(44000, 55152); + +pg_advisory_lock +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + -- the tests are written with the logic where single shard SELECTs + -- do not to open transaction blocks + SET citus.select_opens_transaction_block TO false; + +step s1-select: + SELECT count(*) FROM isolation_table WHERE id = 5; + +count +--------------------------------------------------------------------- + 0 +(1 row) + +step s2-begin: + BEGIN; + +step s2-isolate-tenant: + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'force_logical'); + +step s1-insert: + INSERT INTO isolation_table VALUES (5, 10); + +step s1-commit: + COMMIT; + +step s3-release-advisory-lock: + SELECT pg_advisory_unlock(44000, 55152); + +pg_advisory_unlock +--------------------------------------------------------------------- +t +(1 row) + +step s2-isolate-tenant: <... completed> +isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1500088 +(1 row) + +step s2-commit: + COMMIT; + +step s2-print-cluster: + -- row count per shard + SELECT + nodeport, shardid, success, result + FROM + run_command_on_placements('isolation_table', 'select count(*) from %s') + ORDER BY + nodeport, shardid; + -- rows + SELECT id, value FROM isolation_table ORDER BY id, value; + +nodeport|shardid|success|result +--------------------------------------------------------------------- + 57637|1500087|t | 0 + 57637|1500088|t | 1 + 57637|1500089|t | 0 + 57638|1500086|t | 0 +(4 rows) + +id|value +--------------------------------------------------------------------- + 5| 10 +(1 row) + + +starting permutation: s1-load-cache s3-acquire-advisory-lock s1-begin s1-select s2-begin s2-isolate-tenant s1-copy s1-commit s3-release-advisory-lock s2-commit s2-print-cluster +create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +step s1-load-cache: + TRUNCATE isolation_table; + +step s3-acquire-advisory-lock: + SELECT pg_advisory_lock(44000, 55152); + +pg_advisory_lock +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + -- the tests are written with the logic where single shard SELECTs + -- do not to open transaction blocks + SET citus.select_opens_transaction_block TO false; + +step s1-select: + SELECT count(*) FROM isolation_table WHERE id = 5; + +count +--------------------------------------------------------------------- + 0 +(1 row) + +step s2-begin: + BEGIN; + +step s2-isolate-tenant: + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'force_logical'); + +step s1-copy: + COPY isolation_table FROM PROGRAM 'echo "1,1\n2,2\n3,3\n4,4\n5,5"' WITH CSV; + +step s1-commit: + COMMIT; + +step s3-release-advisory-lock: + SELECT pg_advisory_unlock(44000, 55152); + +pg_advisory_unlock +--------------------------------------------------------------------- +t +(1 row) + +step s2-isolate-tenant: <... completed> +isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1500094 +(1 row) + +step s2-commit: + COMMIT; + +step s2-print-cluster: + -- row count per shard + SELECT + nodeport, shardid, success, result + FROM + run_command_on_placements('isolation_table', 'select count(*) from %s') + ORDER BY + nodeport, shardid; + -- rows + SELECT id, value FROM isolation_table ORDER BY id, value; + +nodeport|shardid|success|result +--------------------------------------------------------------------- + 57637|1500093|t | 1 + 57637|1500094|t | 1 + 57637|1500095|t | 2 + 57638|1500092|t | 1 +(4 rows) + +id|value +--------------------------------------------------------------------- + 1| 1 + 2| 2 + 3| 3 + 4| 4 + 5| 5 +(5 rows) + + +starting permutation: s1-insert s3-acquire-advisory-lock s1-begin s1-select s2-begin s2-isolate-tenant s1-update s1-commit s3-release-advisory-lock s2-commit s2-print-cluster +create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +step s1-insert: + INSERT INTO isolation_table VALUES (5, 10); + +step s3-acquire-advisory-lock: + SELECT pg_advisory_lock(44000, 55152); + +pg_advisory_lock +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + -- the tests are written with the logic where single shard SELECTs + -- do not to open transaction blocks + SET citus.select_opens_transaction_block TO false; + +step s1-select: + SELECT count(*) FROM isolation_table WHERE id = 5; + +count +--------------------------------------------------------------------- + 1 +(1 row) + +step s2-begin: + BEGIN; + +step s2-isolate-tenant: + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'force_logical'); + +step s1-update: + UPDATE isolation_table SET value = 5 WHERE id = 5; + +step s1-commit: + COMMIT; + +step s3-release-advisory-lock: + SELECT pg_advisory_unlock(44000, 55152); + +pg_advisory_unlock +--------------------------------------------------------------------- +t +(1 row) + +step s2-isolate-tenant: <... completed> +isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1500100 +(1 row) + +step s2-commit: + COMMIT; + +step s2-print-cluster: + -- row count per shard + SELECT + nodeport, shardid, success, result + FROM + run_command_on_placements('isolation_table', 'select count(*) from %s') + ORDER BY + nodeport, shardid; + -- rows + SELECT id, value FROM isolation_table ORDER BY id, value; + +nodeport|shardid|success|result +--------------------------------------------------------------------- + 57637|1500099|t | 0 + 57637|1500100|t | 1 + 57637|1500101|t | 0 + 57638|1500098|t | 0 +(4 rows) + +id|value +--------------------------------------------------------------------- + 5| 5 +(1 row) + + +starting permutation: s1-insert s3-acquire-advisory-lock s1-begin s1-select s2-begin s2-isolate-tenant s1-delete s1-commit s3-release-advisory-lock s2-commit s2-print-cluster +create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +step s1-insert: + INSERT INTO isolation_table VALUES (5, 10); + +step s3-acquire-advisory-lock: + SELECT pg_advisory_lock(44000, 55152); + +pg_advisory_lock +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + -- the tests are written with the logic where single shard SELECTs + -- do not to open transaction blocks + SET citus.select_opens_transaction_block TO false; + +step s1-select: + SELECT count(*) FROM isolation_table WHERE id = 5; + +count +--------------------------------------------------------------------- + 1 +(1 row) + +step s2-begin: + BEGIN; + +step s2-isolate-tenant: + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'force_logical'); + +step s1-delete: + DELETE FROM isolation_table WHERE id = 5; + +step s1-commit: + COMMIT; + +step s3-release-advisory-lock: + SELECT pg_advisory_unlock(44000, 55152); + +pg_advisory_unlock +--------------------------------------------------------------------- +t +(1 row) + +step s2-isolate-tenant: <... completed> +isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1500106 +(1 row) + +step s2-commit: + COMMIT; + +step s2-print-cluster: + -- row count per shard + SELECT + nodeport, shardid, success, result + FROM + run_command_on_placements('isolation_table', 'select count(*) from %s') + ORDER BY + nodeport, shardid; + -- rows + SELECT id, value FROM isolation_table ORDER BY id, value; + +nodeport|shardid|success|result +--------------------------------------------------------------------- + 57637|1500105|t | 0 + 57637|1500106|t | 0 + 57637|1500107|t | 0 + 57638|1500104|t | 0 +(4 rows) + +id|value +--------------------------------------------------------------------- +(0 rows) + + +starting permutation: s3-acquire-advisory-lock s1-begin s1-select s2-begin s2-isolate-tenant s1-insert s1-commit s3-release-advisory-lock s2-commit s2-print-cluster +create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +step s3-acquire-advisory-lock: + SELECT pg_advisory_lock(44000, 55152); + +pg_advisory_lock +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + -- the tests are written with the logic where single shard SELECTs + -- do not to open transaction blocks + SET citus.select_opens_transaction_block TO false; + +step s1-select: + SELECT count(*) FROM isolation_table WHERE id = 5; + +count +--------------------------------------------------------------------- + 0 +(1 row) + +step s2-begin: + BEGIN; + +step s2-isolate-tenant: + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'force_logical'); + +step s1-insert: + INSERT INTO isolation_table VALUES (5, 10); + +step s1-commit: + COMMIT; + +step s3-release-advisory-lock: + SELECT pg_advisory_unlock(44000, 55152); + +pg_advisory_unlock +--------------------------------------------------------------------- +t +(1 row) + +step s2-isolate-tenant: <... completed> +isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1500112 +(1 row) + +step s2-commit: + COMMIT; + +step s2-print-cluster: + -- row count per shard + SELECT + nodeport, shardid, success, result + FROM + run_command_on_placements('isolation_table', 'select count(*) from %s') + ORDER BY + nodeport, shardid; + -- rows + SELECT id, value FROM isolation_table ORDER BY id, value; + +nodeport|shardid|success|result +--------------------------------------------------------------------- + 57637|1500111|t | 0 + 57637|1500112|t | 1 + 57637|1500113|t | 0 + 57638|1500110|t | 0 +(4 rows) + +id|value +--------------------------------------------------------------------- + 5| 10 +(1 row) + + +starting permutation: s3-acquire-advisory-lock s1-begin s1-select s2-begin s2-isolate-tenant s1-copy s1-commit s3-release-advisory-lock s2-commit s2-print-cluster +create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +step s3-acquire-advisory-lock: + SELECT pg_advisory_lock(44000, 55152); + +pg_advisory_lock +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + -- the tests are written with the logic where single shard SELECTs + -- do not to open transaction blocks + SET citus.select_opens_transaction_block TO false; + +step s1-select: + SELECT count(*) FROM isolation_table WHERE id = 5; + +count +--------------------------------------------------------------------- + 0 +(1 row) + +step s2-begin: + BEGIN; + +step s2-isolate-tenant: + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'force_logical'); + +step s1-copy: + COPY isolation_table FROM PROGRAM 'echo "1,1\n2,2\n3,3\n4,4\n5,5"' WITH CSV; + +step s1-commit: + COMMIT; + +step s3-release-advisory-lock: + SELECT pg_advisory_unlock(44000, 55152); + +pg_advisory_unlock +--------------------------------------------------------------------- +t +(1 row) + +step s2-isolate-tenant: <... completed> +isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1500118 +(1 row) + +step s2-commit: + COMMIT; + +step s2-print-cluster: + -- row count per shard + SELECT + nodeport, shardid, success, result + FROM + run_command_on_placements('isolation_table', 'select count(*) from %s') + ORDER BY + nodeport, shardid; + -- rows + SELECT id, value FROM isolation_table ORDER BY id, value; + +nodeport|shardid|success|result +--------------------------------------------------------------------- + 57637|1500117|t | 1 + 57637|1500118|t | 1 + 57637|1500119|t | 2 + 57638|1500116|t | 1 +(4 rows) + +id|value +--------------------------------------------------------------------- + 1| 1 + 2| 2 + 3| 3 + 4| 4 + 5| 5 +(5 rows) + + +starting permutation: s1-load-cache s1-insert s3-acquire-advisory-lock s1-begin s1-isolate-tenant s2-isolate-tenant s3-release-advisory-lock s1-commit s2-print-cluster +create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +step s1-load-cache: + TRUNCATE isolation_table; + +step s1-insert: + INSERT INTO isolation_table VALUES (5, 10); + +step s3-acquire-advisory-lock: + SELECT pg_advisory_lock(44000, 55152); + +pg_advisory_lock +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + -- the tests are written with the logic where single shard SELECTs + -- do not to open transaction blocks + SET citus.select_opens_transaction_block TO false; + +step s1-isolate-tenant: + SELECT isolate_tenant_to_new_shard('isolation_table', 2, shard_transfer_mode => 'force_logical'); + +step s2-isolate-tenant: + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'force_logical'); + +ERROR: could not acquire the lock required to split public.isolation_table +step s3-release-advisory-lock: + SELECT pg_advisory_unlock(44000, 55152); + +pg_advisory_unlock +--------------------------------------------------------------------- +t +(1 row) + +step s1-isolate-tenant: <... completed> +isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1500124 +(1 row) + +step s1-commit: + COMMIT; + +step s2-print-cluster: + -- row count per shard + SELECT + nodeport, shardid, success, result + FROM + run_command_on_placements('isolation_table', 'select count(*) from %s') + ORDER BY + nodeport, shardid; + -- rows + SELECT id, value FROM isolation_table ORDER BY id, value; + +nodeport|shardid|success|result +--------------------------------------------------------------------- + 57637|1500121|t | 1 + 57638|1500123|t | 0 + 57638|1500124|t | 0 + 57638|1500125|t | 0 +(4 rows) + +id|value +--------------------------------------------------------------------- + 5| 10 +(1 row) + + +starting permutation: s1-insert s3-acquire-advisory-lock s1-begin s1-isolate-tenant s2-isolate-tenant s3-release-advisory-lock s1-commit s2-print-cluster +create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +step s1-insert: + INSERT INTO isolation_table VALUES (5, 10); + +step s3-acquire-advisory-lock: + SELECT pg_advisory_lock(44000, 55152); + +pg_advisory_lock +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + -- the tests are written with the logic where single shard SELECTs + -- do not to open transaction blocks + SET citus.select_opens_transaction_block TO false; + +step s1-isolate-tenant: + SELECT isolate_tenant_to_new_shard('isolation_table', 2, shard_transfer_mode => 'force_logical'); + +step s2-isolate-tenant: + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'force_logical'); + +ERROR: could not acquire the lock required to split public.isolation_table +step s3-release-advisory-lock: + SELECT pg_advisory_unlock(44000, 55152); + +pg_advisory_unlock +--------------------------------------------------------------------- +t +(1 row) + +step s1-isolate-tenant: <... completed> +isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1500130 +(1 row) + +step s1-commit: + COMMIT; + +step s2-print-cluster: + -- row count per shard + SELECT + nodeport, shardid, success, result + FROM + run_command_on_placements('isolation_table', 'select count(*) from %s') + ORDER BY + nodeport, shardid; + -- rows + SELECT id, value FROM isolation_table ORDER BY id, value; + +nodeport|shardid|success|result +--------------------------------------------------------------------- + 57637|1500127|t | 1 + 57638|1500129|t | 0 + 57638|1500130|t | 0 + 57638|1500131|t | 0 +(4 rows) + +id|value +--------------------------------------------------------------------- + 5| 10 +(1 row) + diff --git a/src/test/regress/expected/isolation_tenant_isolation_with_fkey_to_reference.out b/src/test/regress/expected/isolation_tenant_isolation_with_fkey_to_reference.out index 14ff5ca51..808c1c9eb 100644 --- a/src/test/regress/expected/isolation_tenant_isolation_with_fkey_to_reference.out +++ b/src/test/regress/expected/isolation_tenant_isolation_with_fkey_to_reference.out @@ -16,7 +16,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -73,7 +73,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -130,7 +130,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -187,7 +187,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- @@ -244,7 +244,7 @@ step s2-begin: BEGIN; step s2-isolate-tenant: - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- diff --git a/src/test/regress/expected/multi_create_table.out b/src/test/regress/expected/multi_create_table.out index 5bb16273b..80fb3a2b2 100644 --- a/src/test/regress/expected/multi_create_table.out +++ b/src/test/regress/expected/multi_create_table.out @@ -400,7 +400,7 @@ SELECT create_distributed_table ('shard_split_table', 'a'); (1 row) -SELECT 1 FROM isolate_tenant_to_new_shard('shard_split_table', 5); +SELECT 1 FROM isolate_tenant_to_new_shard('shard_split_table', 5, shard_transfer_mode => 'block_writes'); ?column? --------------------------------------------------------------------- 1 diff --git a/src/test/regress/expected/multi_extension.out b/src/test/regress/expected/multi_extension.out index 03ad6d794..b1eddd136 100644 --- a/src/test/regress/expected/multi_extension.out +++ b/src/test/regress/expected/multi_extension.out @@ -1081,6 +1081,7 @@ SELECT * FROM multi_extension.print_extension_changes(); function citus_internal.downgrade_columnar_storage(regclass) void | function citus_internal.upgrade_columnar_storage(regclass) void | function columnar.columnar_handler(internal) table_am_handler | + function isolate_tenant_to_new_shard(regclass,"any",text) bigint | function replicate_reference_tables() void | function worker_cleanup_job_schema_cache() void | function worker_create_schema(bigint,text) void | @@ -1098,6 +1099,7 @@ SELECT * FROM multi_extension.print_extension_changes(); table columnar.stripe | | function citus_locks() SETOF record | function citus_split_shard_by_split_points(bigint,text[],integer[],citus.shard_transfer_mode) void + | function isolate_tenant_to_new_shard(regclass,"any",text,citus.shard_transfer_mode) bigint | function replicate_reference_tables(citus.shard_transfer_mode) void | function worker_copy_table_to_node(regclass,integer) void | function worker_split_copy(bigint,split_copy_info[]) void @@ -1106,7 +1108,7 @@ SELECT * FROM multi_extension.print_extension_changes(); | type split_copy_info | type split_shard_info | view citus_locks -(32 rows) +(34 rows) DROP TABLE multi_extension.prev_objects, multi_extension.extension_diff; -- show running version diff --git a/src/test/regress/expected/multi_multiuser_master_protocol.out b/src/test/regress/expected/multi_multiuser_master_protocol.out index a249e72c1..b0d90a6c5 100644 --- a/src/test/regress/expected/multi_multiuser_master_protocol.out +++ b/src/test/regress/expected/multi_multiuser_master_protocol.out @@ -389,7 +389,7 @@ ORDER BY nodename, nodeport, shardid; (4 rows) -- verify isolate tenant carries grants -SELECT isolate_tenant_to_new_shard('multiuser_schema.hash_table', 5); +SELECT isolate_tenant_to_new_shard('multiuser_schema.hash_table', 5, shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- 109100 diff --git a/src/test/regress/expected/multi_tenant_isolation.out b/src/test/regress/expected/multi_tenant_isolation.out index ee1df6d76..f97dc3fc0 100644 --- a/src/test/regress/expected/multi_tenant_isolation.out +++ b/src/test/regress/expected/multi_tenant_isolation.out @@ -177,19 +177,19 @@ SELECT * FROM pg_dist_shard (4 rows) -- check without cascade option -SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, shard_transfer_mode => 'block_writes'); ERROR: cannot isolate tenant because "lineitem_streaming" has colocated tables HINT: Use CASCADE option to isolate tenants for the colocated tables too. Example usage: isolate_tenant_to_new_shard('lineitem_streaming', '100', 'CASCADE') -- check with an input not castable to bigint -SELECT isolate_tenant_to_new_shard('lineitem_streaming', 'abc', 'CASCADE'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 'abc', 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: invalid input syntax for type bigint: "abc" -SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, 'CASCADE', shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- 1230005 (1 row) -SELECT isolate_tenant_to_new_shard('lineitem_streaming', 101, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 101, 'CASCADE', shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- 1230011 @@ -221,53 +221,53 @@ SELECT start_metadata_sync_to_node('localhost', :worker_1_port); SET search_path to "Tenant Isolation"; -- test a failing transaction block BEGIN; -SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE', shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- 1230020 (1 row) -SELECT isolate_tenant_to_new_shard('lineitem_streaming', 102, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 102, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: table lineitem_streaming has already been isolated for the given value COMMIT; -- test a rollback transaction block BEGIN; -SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE', shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- 1230026 (1 row) -SELECT isolate_tenant_to_new_shard('orders_streaming', 103, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 103, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: cannot isolate tenant after other modifications in the same transaction. ROLLBACK; -- test a succesfull transaction block BEGIN; -SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE', shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- 1230032 (1 row) COMMIT; -SELECT isolate_tenant_to_new_shard('orders_streaming', 103, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 103, 'CASCADE', shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- 1230038 (1 row) -SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: table lineitem_streaming has already been isolated for the given value -SELECT isolate_tenant_to_new_shard('orders_streaming', 101, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 101, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: table orders_streaming has already been isolated for the given value -- test corner cases: hash(-1995148554) = -2147483648 and hash(-1686493264) = 2147483647 -SELECT isolate_tenant_to_new_shard('lineitem_streaming', -1995148554, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', -1995148554, 'CASCADE', shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- 1230040 (1 row) -SELECT isolate_tenant_to_new_shard('orders_streaming', -1686493264, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', -1686493264, 'CASCADE', shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- 1230047 @@ -622,11 +622,11 @@ SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1997-08-08'; 1 (1 row) -SELECT isolate_tenant_to_new_shard('lineitem_date', '1998-05-26'); +SELECT isolate_tenant_to_new_shard('lineitem_date', '1998-05-26', shard_transfer_mode => 'block_writes'); ERROR: cannot isolate tenants when using shard replication -SELECT isolate_tenant_to_new_shard('lineitem_date', '1997-07-30'); +SELECT isolate_tenant_to_new_shard('lineitem_date', '1997-07-30', shard_transfer_mode => 'block_writes'); ERROR: cannot isolate tenants when using shard replication -SELECT isolate_tenant_to_new_shard('lineitem_date', '1998-01-15'); +SELECT isolate_tenant_to_new_shard('lineitem_date', '1998-01-15', shard_transfer_mode => 'block_writes'); ERROR: cannot isolate tenants when using shard replication SELECT count(*) FROM lineitem_date; count @@ -662,7 +662,7 @@ SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1997-08-08'; \c - postgres - :master_port SET search_path to "Tenant Isolation"; UPDATE pg_dist_shard_placement SET shardstate = 3 WHERE nodeport = :worker_1_port; -SELECT isolate_tenant_to_new_shard('lineitem_date', '1997-08-08'); +SELECT isolate_tenant_to_new_shard('lineitem_date', '1997-08-08', shard_transfer_mode => 'block_writes'); ERROR: cannot split shard because relation "lineitem_date" has an inactive shard placement for the shard xxxxx HINT: Use master_copy_shard_placement UDF to repair the inactive shard placement. UPDATE pg_dist_shard_placement SET shardstate = 1 WHERE nodeport = :worker_1_port; @@ -679,7 +679,7 @@ SELECT create_distributed_table('test_append', 'tenant_id', 'append'); (1 row) -SELECT isolate_tenant_to_new_shard('test_append', 100); +SELECT isolate_tenant_to_new_shard('test_append', 100, shard_transfer_mode => 'block_writes'); ERROR: cannot isolate tenant because tenant isolation is only support for hash distributed tables -- check metadata for comparison SELECT * FROM pg_dist_shard @@ -752,7 +752,7 @@ SET citus.override_table_visibility TO false; \c - mx_isolation_role_ent - :master_port SET search_path to "Tenant Isolation"; \set VERBOSITY terse -SELECT isolate_tenant_to_new_shard('orders_streaming', 104, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 104, 'CASCADE', shard_transfer_mode => 'block_writes'); ERROR: command CREATE TABLE is disabled \set VERBOSITY default \c - postgres - :worker_1_port @@ -795,7 +795,7 @@ CREATE EVENT TRIGGER abort_drop ON sql_drop \c - mx_isolation_role_ent - :master_port SET search_path to "Tenant Isolation"; \set VERBOSITY terse -SELECT isolate_tenant_to_new_shard('orders_streaming', 104, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 104, 'CASCADE', shard_transfer_mode => 'block_writes'); WARNING: command DROP TABLE is disabled WARNING: command DROP TABLE is disabled WARNING: command DROP TABLE is disabled @@ -1014,7 +1014,7 @@ SELECT create_distributed_table('composite_table', 'composite_key'); INSERT INTO composite_table VALUES ('(1, 2)'::test_composite_type); INSERT INTO composite_table VALUES ('(1, 3)'::test_composite_type); INSERT INTO composite_table VALUES ('(1, 4)'::test_composite_type); -SELECT isolate_tenant_to_new_shard('composite_table', '(1, 3)'); +SELECT isolate_tenant_to_new_shard('composite_table', '(1, 3)', shard_transfer_mode => 'block_writes'); ERROR: cannot isolate tenants when using shard replication SELECT count(*) FROM composite_table WHERE composite_key = '(1, 2)'::test_composite_type; count @@ -1072,7 +1072,7 @@ INSERT INTO test_reference_table_fkey SELECT i FROM generate_series (0, 100) i; INSERT INTO test_colocated_table_1 SELECT i, i FROM generate_series (0, 100) i; INSERT INTO test_colocated_table_2 SELECT i, i FROM generate_series (0, 100) i; INSERT INTO test_colocated_table_3 SELECT i, i FROM generate_series (0, 100) i; -SELECT isolate_tenant_to_new_shard('test_colocated_table_2', 1, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('test_colocated_table_2', 1, 'CASCADE', shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- 1230095 @@ -1193,7 +1193,7 @@ SELECT count(*) FROM partitioning_test; (1 row) -- isolate a value into its own shard -SELECT 1 FROM isolate_tenant_to_new_shard('partitioning_test', 2, 'CASCADE'); +SELECT 1 FROM isolate_tenant_to_new_shard('partitioning_test', 2, 'CASCADE', shard_transfer_mode => 'block_writes'); ?column? --------------------------------------------------------------------- 1 @@ -1227,7 +1227,7 @@ SELECT count(*) FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE lo \c - mx_isolation_role_ent - :master_port SET search_path to "Tenant Isolation"; -SELECT 1 FROM isolate_tenant_to_new_shard('test_colocated_table_2', 2, 'CASCADE'); +SELECT 1 FROM isolate_tenant_to_new_shard('test_colocated_table_2', 2, 'CASCADE', shard_transfer_mode => 'block_writes'); ?column? --------------------------------------------------------------------- 1 @@ -1248,6 +1248,8 @@ SELECT 1 FROM master_remove_node('localhost', :master_port); SET client_min_messages TO WARNING; DROP SCHEMA "Tenant Isolation" CASCADE; +REVOKE ALL ON SCHEMA public FROM mx_isolation_role_ent; +DROP ROLE mx_isolation_role_ent; -- stop & resync and stop syncing metadata SELECT stop_metadata_sync_to_node('localhost', :worker_1_port); stop_metadata_sync_to_node diff --git a/src/test/regress/expected/multi_tenant_isolation_nonblocking.out b/src/test/regress/expected/multi_tenant_isolation_nonblocking.out new file mode 100644 index 000000000..01e3bb88e --- /dev/null +++ b/src/test/regress/expected/multi_tenant_isolation_nonblocking.out @@ -0,0 +1,1303 @@ +-- +-- MULTI_TENANT_ISOLATION +-- +-- Tests tenant isolation feature +-- +ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 1230000; +SELECT nextval('pg_catalog.pg_dist_placement_placementid_seq') AS last_placement_id +\gset +ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART 100000; +CREATE SCHEMA "Tenant Isolation"; +SET search_path to "Tenant Isolation"; +CREATE ROLE mx_isolation_role_ent WITH LOGIN; +GRANT ALL ON SCHEMA "Tenant Isolation", public TO mx_isolation_role_ent; +-- connect with this new role +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; +SET citus.shard_replication_factor TO 1; +SET citus.shard_count to 2; +CREATE TABLE lineitem_streaming ( + l_orderkey bigint not null, + l_partkey integer not null, + l_suppkey integer not null, + l_linenumber integer not null, + l_quantity decimal(15, 2) not null, + l_extendedprice decimal(15, 2) not null, + l_discount decimal(15, 2) not null, + l_tax decimal(15, 2) not null, + l_returnflag char(1) not null, + l_linestatus char(1) not null, + l_shipdate date not null, + l_commitdate date not null, + l_receiptdate date not null, + l_shipinstruct char(25) not null, + l_shipmode char(10) not null, + l_comment varchar(44) not null); +SELECT create_distributed_table('lineitem_streaming', 'l_orderkey'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE orders_streaming ( + o_orderkey bigint not null primary key, + o_custkey integer not null, + o_orderstatus char(1) not null, + o_totalprice decimal(15,2) not null, + o_orderdate date not null, + o_orderpriority char(15) not null, + o_clerk char(15) not null, + o_shippriority integer not null, + o_comment varchar(79) not null); +SELECT create_distributed_table('orders_streaming', 'o_orderkey'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +\COPY lineitem_streaming FROM STDIN WITH DELIMITER '|' +\COPY orders_streaming FROM STDIN WITH DELIMITER '|' +ALTER TABLE lineitem_streaming ADD CONSTRAINT test_constraint + FOREIGN KEY(l_orderkey) REFERENCES orders_streaming(o_orderkey); +-- test failing foreign constraints +\COPY lineitem_streaming FROM STDIN WITH DELIMITER '|' +ERROR: insert or update on table "lineitem_streaming_1230001" violates foreign key constraint "test_constraint_1230001" +DETAIL: Key (l_orderkey)=(128) is not present in table "orders_streaming_1230003". +-- tests for cluster health +SELECT count(*) FROM lineitem_streaming; + count +--------------------------------------------------------------------- + 22 +(1 row) + +SELECT count(*) FROM orders_streaming; + count +--------------------------------------------------------------------- + 7 +(1 row) + +SELECT + l_orderkey, + sum(l_extendedprice * (1 - l_discount)) as revenue, + o_orderdate +FROM + orders_streaming, + lineitem_streaming +WHERE + l_orderkey = o_orderkey +GROUP BY + l_orderkey, + o_orderdate +ORDER BY + revenue DESC, + o_orderdate; + l_orderkey | revenue | o_orderdate +--------------------------------------------------------------------- + 100 | 181042.2683 | 02-28-1998 + 102 | 159639.9677 | 05-09-1997 + 101 | 124074.5328 | 03-17-1996 + 103 | 119741.5469 | 06-20-1996 + 99 | 109604.3256 | 03-13-1994 + -1995148554 | 16890.6816 | 05-08-1995 + -1686493264 | 1988.7134 | 09-05-1997 +(7 rows) + +-- Checks to see if metadata and data are isolated properly. If there are problems in +-- metadata and/or data on workers, these queries should return different results below +-- after tenant isolation operations are applied. +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 99; + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 100; + count +--------------------------------------------------------------------- + 5 +(1 row) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 101; + count +--------------------------------------------------------------------- + 3 +(1 row) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 102; + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 103; + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 99; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 100; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 101; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 102; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 103; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT * FROM pg_dist_shard + WHERE logicalrelid = 'lineitem_streaming'::regclass OR logicalrelid = 'orders_streaming'::regclass + ORDER BY shardminvalue::BIGINT, logicalrelid; + logicalrelid | shardid | shardstorage | shardminvalue | shardmaxvalue +--------------------------------------------------------------------- + lineitem_streaming | 1230000 | t | -2147483648 | -1 + orders_streaming | 1230002 | t | -2147483648 | -1 + lineitem_streaming | 1230001 | t | 0 | 2147483647 + orders_streaming | 1230003 | t | 0 | 2147483647 +(4 rows) + +-- check without cascade option +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, shard_transfer_mode => 'force_logical'); +ERROR: cannot isolate tenant because "lineitem_streaming" has colocated tables +HINT: Use CASCADE option to isolate tenants for the colocated tables too. Example usage: isolate_tenant_to_new_shard('lineitem_streaming', '100', 'CASCADE') +-- check with an input not castable to bigint +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 'abc', 'CASCADE', shard_transfer_mode => 'force_logical'); +ERROR: invalid input syntax for type bigint: "abc" +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, 'CASCADE', shard_transfer_mode => 'force_logical'); + isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1230005 +(1 row) + +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 101, 'CASCADE', shard_transfer_mode => 'force_logical'); + isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1230011 +(1 row) + +-- add an explain check to see if we hit the new isolated shard +EXPLAIN (COSTS false) SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 101; + QUERY PLAN +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + Task Count: 1 + Tasks Shown: All + -> Task + Node: host=localhost port=xxxxx dbname=regression + -> Aggregate + -> Seq Scan on lineitem_streaming_1230011 lineitem_streaming + Filter: (l_orderkey = 101) +(8 rows) + +-- create an MX node +\c - postgres - :master_port +SELECT start_metadata_sync_to_node('localhost', :worker_1_port); + start_metadata_sync_to_node +--------------------------------------------------------------------- + +(1 row) + +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; +-- test a failing transaction block +BEGIN; +SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE', shard_transfer_mode => 'force_logical'); + isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1230020 +(1 row) + +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 102, 'CASCADE', shard_transfer_mode => 'force_logical'); +ERROR: table lineitem_streaming has already been isolated for the given value +COMMIT; +-- test a rollback transaction block +BEGIN; +SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE', shard_transfer_mode => 'force_logical'); + isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1230026 +(1 row) + +SELECT isolate_tenant_to_new_shard('orders_streaming', 103, 'CASCADE', shard_transfer_mode => 'force_logical'); +ERROR: multiple shard movements/splits via logical replication in the same transaction is currently not supported +ROLLBACK; +-- test a succesfull transaction block +BEGIN; +SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE', shard_transfer_mode => 'force_logical'); + isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1230032 +(1 row) + +COMMIT; +SELECT isolate_tenant_to_new_shard('orders_streaming', 103, 'CASCADE', shard_transfer_mode => 'force_logical'); + isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1230038 +(1 row) + +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, 'CASCADE', shard_transfer_mode => 'force_logical'); +ERROR: table lineitem_streaming has already been isolated for the given value +SELECT isolate_tenant_to_new_shard('orders_streaming', 101, 'CASCADE', shard_transfer_mode => 'force_logical'); +ERROR: table orders_streaming has already been isolated for the given value +-- test corner cases: hash(-1995148554) = -2147483648 and hash(-1686493264) = 2147483647 +SELECT isolate_tenant_to_new_shard('lineitem_streaming', -1995148554, 'CASCADE', shard_transfer_mode => 'force_logical'); + isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1230040 +(1 row) + +SELECT isolate_tenant_to_new_shard('orders_streaming', -1686493264, 'CASCADE', shard_transfer_mode => 'force_logical'); + isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1230047 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = -1995148554; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = -1686493264; + count +--------------------------------------------------------------------- + 1 +(1 row) + +-- tests for cluster health +SELECT count(*) FROM lineitem_streaming; + count +--------------------------------------------------------------------- + 22 +(1 row) + +SELECT count(*) FROM orders_streaming; + count +--------------------------------------------------------------------- + 7 +(1 row) + +SELECT + l_orderkey, + sum(l_extendedprice * (1 - l_discount)) as revenue, + o_orderdate +FROM + orders_streaming, + lineitem_streaming +WHERE + l_orderkey = o_orderkey +GROUP BY + l_orderkey, + o_orderdate +ORDER BY + revenue DESC, + o_orderdate; + l_orderkey | revenue | o_orderdate +--------------------------------------------------------------------- + 100 | 181042.2683 | 02-28-1998 + 102 | 159639.9677 | 05-09-1997 + 101 | 124074.5328 | 03-17-1996 + 103 | 119741.5469 | 06-20-1996 + 99 | 109604.3256 | 03-13-1994 + -1995148554 | 16890.6816 | 05-08-1995 + -1686493264 | 1988.7134 | 09-05-1997 +(7 rows) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 99; + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 100; + count +--------------------------------------------------------------------- + 5 +(1 row) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 101; + count +--------------------------------------------------------------------- + 3 +(1 row) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 102; + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 103; + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 99; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 100; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 101; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 102; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 103; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT * FROM pg_dist_shard + WHERE logicalrelid = 'lineitem_streaming'::regclass OR logicalrelid = 'orders_streaming'::regclass + ORDER BY shardminvalue::BIGINT, logicalrelid; + logicalrelid | shardid | shardstorage | shardminvalue | shardmaxvalue +--------------------------------------------------------------------- + lineitem_streaming | 1230040 | t | -2147483648 | -2147483648 + orders_streaming | 1230042 | t | -2147483648 | -2147483648 + lineitem_streaming | 1230041 | t | -2147483647 | -136164586 + orders_streaming | 1230043 | t | -2147483647 | -136164586 + lineitem_streaming | 1230035 | t | -136164585 | -136164585 + orders_streaming | 1230038 | t | -136164585 | -136164585 + lineitem_streaming | 1230036 | t | -136164584 | -85071815 + orders_streaming | 1230039 | t | -136164584 | -85071815 + lineitem_streaming | 1230011 | t | -85071814 | -85071814 + orders_streaming | 1230014 | t | -85071814 | -85071814 + lineitem_streaming | 1230012 | t | -85071813 | -1 + orders_streaming | 1230015 | t | -85071813 | -1 + lineitem_streaming | 1230004 | t | 0 | 108199380 + orders_streaming | 1230007 | t | 0 | 108199380 + lineitem_streaming | 1230005 | t | 108199381 | 108199381 + orders_streaming | 1230008 | t | 108199381 | 108199381 + lineitem_streaming | 1230028 | t | 108199382 | 412880111 + orders_streaming | 1230031 | t | 108199382 | 412880111 + lineitem_streaming | 1230029 | t | 412880112 | 412880112 + orders_streaming | 1230032 | t | 412880112 | 412880112 + lineitem_streaming | 1230044 | t | 412880113 | 2147483646 + orders_streaming | 1230046 | t | 412880113 | 2147483646 + lineitem_streaming | 1230045 | t | 2147483647 | 2147483647 + orders_streaming | 1230047 | t | 2147483647 | 2147483647 +(24 rows) + +SELECT * FROM pg_dist_shard_placement WHERE shardid >= 1230000 ORDER BY nodeport, shardid; + shardid | shardstate | shardlength | nodename | nodeport | placementid +--------------------------------------------------------------------- + 1230011 | 1 | 0 | localhost | 57637 | 100011 + 1230012 | 1 | 0 | localhost | 57637 | 100012 + 1230014 | 1 | 0 | localhost | 57637 | 100014 + 1230015 | 1 | 0 | localhost | 57637 | 100015 + 1230035 | 1 | 0 | localhost | 57637 | 100035 + 1230036 | 1 | 0 | localhost | 57637 | 100036 + 1230038 | 1 | 0 | localhost | 57637 | 100038 + 1230039 | 1 | 0 | localhost | 57637 | 100039 + 1230040 | 1 | 0 | localhost | 57637 | 100040 + 1230041 | 1 | 0 | localhost | 57637 | 100041 + 1230042 | 1 | 0 | localhost | 57637 | 100042 + 1230043 | 1 | 0 | localhost | 57637 | 100043 + 1230004 | 1 | 0 | localhost | 57638 | 100004 + 1230005 | 1 | 0 | localhost | 57638 | 100005 + 1230007 | 1 | 0 | localhost | 57638 | 100007 + 1230008 | 1 | 0 | localhost | 57638 | 100008 + 1230028 | 1 | 0 | localhost | 57638 | 100028 + 1230029 | 1 | 0 | localhost | 57638 | 100029 + 1230031 | 1 | 0 | localhost | 57638 | 100031 + 1230032 | 1 | 0 | localhost | 57638 | 100032 + 1230044 | 1 | 0 | localhost | 57638 | 100044 + 1230045 | 1 | 0 | localhost | 57638 | 100045 + 1230046 | 1 | 0 | localhost | 57638 | 100046 + 1230047 | 1 | 0 | localhost | 57638 | 100047 +(24 rows) + +-- test failing foreign constraints after multiple tenant isolation +\COPY lineitem_streaming FROM STDIN WITH DELIMITER '|' +ERROR: insert or update on table "lineitem_streaming_1230044" violates foreign key constraint "test_constraint_1230044" +DETAIL: Key (l_orderkey)=(128) is not present in table "orders_streaming_1230046". +-- connect to the worker node with metadata +\c - mx_isolation_role_ent - :worker_1_port +SET search_path to "Tenant Isolation"; +-- check mx tables +SELECT count(*) FROM lineitem_streaming; + count +--------------------------------------------------------------------- + 22 +(1 row) + +SELECT count(*) FROM orders_streaming; + count +--------------------------------------------------------------------- + 7 +(1 row) + +SELECT + l_orderkey, + sum(l_extendedprice * (1 - l_discount)) as revenue, + o_orderdate +FROM + orders_streaming, + lineitem_streaming +WHERE + l_orderkey = o_orderkey +GROUP BY + l_orderkey, + o_orderdate +ORDER BY + revenue DESC, + o_orderdate; + l_orderkey | revenue | o_orderdate +--------------------------------------------------------------------- + 100 | 181042.2683 | 02-28-1998 + 102 | 159639.9677 | 05-09-1997 + 101 | 124074.5328 | 03-17-1996 + 103 | 119741.5469 | 06-20-1996 + 99 | 109604.3256 | 03-13-1994 + -1995148554 | 16890.6816 | 05-08-1995 + -1686493264 | 1988.7134 | 09-05-1997 +(7 rows) + +-- check shards +SET citus.override_table_visibility TO false; +\d + List of relations + Schema | Name | Type | Owner +--------------------------------------------------------------------- + Tenant Isolation | lineitem_streaming | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230011 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230012 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230035 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230036 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230040 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230041 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230014 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230015 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230038 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230039 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230042 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230043 | table | mx_isolation_role_ent +(14 rows) + +\c - postgres - :worker_1_port +SET search_path to "Tenant Isolation"; +SELECT "Column", "Type", "Modifiers" FROM public.table_desc WHERE relid='orders_streaming_1230039'::regclass; + Column | Type | Modifiers +--------------------------------------------------------------------- + o_orderkey | bigint | not null + o_custkey | integer | not null + o_orderstatus | character(1) | not null + o_totalprice | numeric(15,2) | not null + o_orderdate | date | not null + o_orderpriority | character(15) | not null + o_clerk | character(15) | not null + o_shippriority | integer | not null + o_comment | character varying(79) | not null +(9 rows) + +\c - mx_isolation_role_ent - :worker_1_port +SET search_path to "Tenant Isolation"; +-- check MX metadata +SELECT * FROM pg_dist_shard + WHERE logicalrelid = 'lineitem_streaming'::regclass OR logicalrelid = 'orders_streaming'::regclass + ORDER BY shardminvalue::BIGINT, logicalrelid; + logicalrelid | shardid | shardstorage | shardminvalue | shardmaxvalue +--------------------------------------------------------------------- + lineitem_streaming | 1230040 | t | -2147483648 | -2147483648 + orders_streaming | 1230042 | t | -2147483648 | -2147483648 + lineitem_streaming | 1230041 | t | -2147483647 | -136164586 + orders_streaming | 1230043 | t | -2147483647 | -136164586 + lineitem_streaming | 1230035 | t | -136164585 | -136164585 + orders_streaming | 1230038 | t | -136164585 | -136164585 + lineitem_streaming | 1230036 | t | -136164584 | -85071815 + orders_streaming | 1230039 | t | -136164584 | -85071815 + lineitem_streaming | 1230011 | t | -85071814 | -85071814 + orders_streaming | 1230014 | t | -85071814 | -85071814 + lineitem_streaming | 1230012 | t | -85071813 | -1 + orders_streaming | 1230015 | t | -85071813 | -1 + lineitem_streaming | 1230004 | t | 0 | 108199380 + orders_streaming | 1230007 | t | 0 | 108199380 + lineitem_streaming | 1230005 | t | 108199381 | 108199381 + orders_streaming | 1230008 | t | 108199381 | 108199381 + lineitem_streaming | 1230028 | t | 108199382 | 412880111 + orders_streaming | 1230031 | t | 108199382 | 412880111 + lineitem_streaming | 1230029 | t | 412880112 | 412880112 + orders_streaming | 1230032 | t | 412880112 | 412880112 + lineitem_streaming | 1230044 | t | 412880113 | 2147483646 + orders_streaming | 1230046 | t | 412880113 | 2147483646 + lineitem_streaming | 1230045 | t | 2147483647 | 2147483647 + orders_streaming | 1230047 | t | 2147483647 | 2147483647 +(24 rows) + +-- return to master node +\c - mx_isolation_role_ent - :master_port +-- test a distribution type which does not have a sql hash function +SET search_path to "Tenant Isolation"; +SET citus.shard_replication_factor TO 2; +SET citus.shard_count to 2; +CREATE TABLE lineitem_date ( + l_orderkey bigint not null, + l_partkey integer not null, + l_suppkey integer not null, + l_linenumber integer not null, + l_quantity decimal(15, 2) not null, + l_extendedprice decimal(15, 2) not null, + l_discount decimal(15, 2) not null, + l_tax decimal(15, 2) not null, + l_returnflag char(1) not null, + l_linestatus char(1) not null, + l_shipdate date not null, + l_commitdate date not null, + l_receiptdate date not null, + l_shipinstruct char(25) not null, + l_shipmode char(10) not null, + l_comment varchar(44) not null); +SELECT create_distributed_table('lineitem_date', 'l_shipdate'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +\COPY lineitem_date FROM STDIN WITH DELIMITER '|' +SELECT count(*) FROM lineitem_date; + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1998-05-26'; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1997-07-30'; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1998-01-15'; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1997-08-08'; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT isolate_tenant_to_new_shard('lineitem_date', '1998-05-26', shard_transfer_mode => 'force_logical'); +ERROR: cannot isolate tenants when using shard replication +SELECT isolate_tenant_to_new_shard('lineitem_date', '1997-07-30', shard_transfer_mode => 'force_logical'); +ERROR: cannot isolate tenants when using shard replication +SELECT isolate_tenant_to_new_shard('lineitem_date', '1998-01-15', shard_transfer_mode => 'force_logical'); +ERROR: cannot isolate tenants when using shard replication +SELECT count(*) FROM lineitem_date; + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1998-05-26'; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1997-07-30'; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1998-01-15'; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1997-08-08'; + count +--------------------------------------------------------------------- + 1 +(1 row) + +-- test with invalid shard placements +\c - postgres - :master_port +SET search_path to "Tenant Isolation"; +UPDATE pg_dist_shard_placement SET shardstate = 3 WHERE nodeport = :worker_1_port; +SELECT isolate_tenant_to_new_shard('lineitem_date', '1997-08-08', shard_transfer_mode => 'force_logical'); +ERROR: cannot split shard because relation "lineitem_date" has an inactive shard placement for the shard xxxxx +HINT: Use master_copy_shard_placement UDF to repair the inactive shard placement. +UPDATE pg_dist_shard_placement SET shardstate = 1 WHERE nodeport = :worker_1_port; +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; +DROP TABLE lineitem_date; +-- test on append distributed table +CREATE TABLE test_append ( + tenant_id integer +); +SELECT create_distributed_table('test_append', 'tenant_id', 'append'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('test_append', 100, shard_transfer_mode => 'force_logical'); +ERROR: cannot isolate tenant because tenant isolation is only support for hash distributed tables +-- check metadata for comparison +SELECT * FROM pg_dist_shard + WHERE logicalrelid = 'lineitem_streaming'::regclass OR logicalrelid = 'orders_streaming'::regclass + ORDER BY shardminvalue::BIGINT, logicalrelid; + logicalrelid | shardid | shardstorage | shardminvalue | shardmaxvalue +--------------------------------------------------------------------- + lineitem_streaming | 1230040 | t | -2147483648 | -2147483648 + orders_streaming | 1230042 | t | -2147483648 | -2147483648 + lineitem_streaming | 1230041 | t | -2147483647 | -136164586 + orders_streaming | 1230043 | t | -2147483647 | -136164586 + lineitem_streaming | 1230035 | t | -136164585 | -136164585 + orders_streaming | 1230038 | t | -136164585 | -136164585 + lineitem_streaming | 1230036 | t | -136164584 | -85071815 + orders_streaming | 1230039 | t | -136164584 | -85071815 + lineitem_streaming | 1230011 | t | -85071814 | -85071814 + orders_streaming | 1230014 | t | -85071814 | -85071814 + lineitem_streaming | 1230012 | t | -85071813 | -1 + orders_streaming | 1230015 | t | -85071813 | -1 + lineitem_streaming | 1230004 | t | 0 | 108199380 + orders_streaming | 1230007 | t | 0 | 108199380 + lineitem_streaming | 1230005 | t | 108199381 | 108199381 + orders_streaming | 1230008 | t | 108199381 | 108199381 + lineitem_streaming | 1230028 | t | 108199382 | 412880111 + orders_streaming | 1230031 | t | 108199382 | 412880111 + lineitem_streaming | 1230029 | t | 412880112 | 412880112 + orders_streaming | 1230032 | t | 412880112 | 412880112 + lineitem_streaming | 1230044 | t | 412880113 | 2147483646 + orders_streaming | 1230046 | t | 412880113 | 2147483646 + lineitem_streaming | 1230045 | t | 2147483647 | 2147483647 + orders_streaming | 1230047 | t | 2147483647 | 2147483647 +(24 rows) + +-- test failure scenarios with triggers on workers +\c - postgres - :worker_1_port +SET search_path to "Tenant Isolation"; +SET citus.enable_metadata_sync TO OFF; +CREATE OR REPLACE FUNCTION abort_any_command() + RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + RAISE EXCEPTION 'command % is disabled', tg_tag; +END; +$$; +RESET citus.enable_metadata_sync; +CREATE EVENT TRIGGER abort_ddl ON ddl_command_end + EXECUTE PROCEDURE abort_any_command(); +SET citus.override_table_visibility TO false; +\d + List of relations + Schema | Name | Type | Owner +--------------------------------------------------------------------- + Tenant Isolation | lineitem_streaming | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230011 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230012 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230035 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230036 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230040 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230041 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230014 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230015 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230038 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230039 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230042 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230043 | table | mx_isolation_role_ent +(14 rows) + +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; +\set VERBOSITY terse +SELECT isolate_tenant_to_new_shard('orders_streaming', 104, 'CASCADE', shard_transfer_mode => 'force_logical'); +ERROR: command CREATE TABLE is disabled +\set VERBOSITY default +\c - postgres - :worker_1_port +SET search_path to "Tenant Isolation"; +SET citus.override_table_visibility TO false; +\d + List of relations + Schema | Name | Type | Owner +--------------------------------------------------------------------- + Tenant Isolation | lineitem_streaming | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230011 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230012 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230035 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230036 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230040 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230041 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230014 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230015 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230038 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230039 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230042 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230043 | table | mx_isolation_role_ent +(14 rows) + +DROP EVENT TRIGGER abort_ddl; +-- create a trigger for drops +SET citus.enable_metadata_sync TO OFF; +CREATE OR REPLACE FUNCTION abort_drop_command() + RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + RAISE EXCEPTION 'command % is disabled', tg_tag; +END; +$$; +RESET citus.enable_metadata_sync; +CREATE EVENT TRIGGER abort_drop ON sql_drop + EXECUTE PROCEDURE abort_drop_command(); +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; +\set VERBOSITY terse +SELECT isolate_tenant_to_new_shard('orders_streaming', 104, 'CASCADE', shard_transfer_mode => 'force_logical'); +WARNING: command DROP TABLE is disabled +WARNING: command DROP TABLE is disabled +WARNING: command DROP TABLE is disabled +WARNING: command DROP TABLE is disabled +WARNING: command DROP TABLE is disabled +WARNING: command DROP TABLE is disabled +ERROR: command DROP SUBSCRIPTION is disabled +\set VERBOSITY default +-- check if metadata is changed +SELECT * FROM pg_dist_shard + WHERE logicalrelid = 'lineitem_streaming'::regclass OR logicalrelid = 'orders_streaming'::regclass + ORDER BY shardminvalue::BIGINT, logicalrelid; + logicalrelid | shardid | shardstorage | shardminvalue | shardmaxvalue +--------------------------------------------------------------------- + lineitem_streaming | 1230040 | t | -2147483648 | -2147483648 + orders_streaming | 1230042 | t | -2147483648 | -2147483648 + lineitem_streaming | 1230041 | t | -2147483647 | -136164586 + orders_streaming | 1230043 | t | -2147483647 | -136164586 + lineitem_streaming | 1230035 | t | -136164585 | -136164585 + orders_streaming | 1230038 | t | -136164585 | -136164585 + lineitem_streaming | 1230036 | t | -136164584 | -85071815 + orders_streaming | 1230039 | t | -136164584 | -85071815 + lineitem_streaming | 1230011 | t | -85071814 | -85071814 + orders_streaming | 1230014 | t | -85071814 | -85071814 + lineitem_streaming | 1230012 | t | -85071813 | -1 + orders_streaming | 1230015 | t | -85071813 | -1 + lineitem_streaming | 1230004 | t | 0 | 108199380 + orders_streaming | 1230007 | t | 0 | 108199380 + lineitem_streaming | 1230005 | t | 108199381 | 108199381 + orders_streaming | 1230008 | t | 108199381 | 108199381 + lineitem_streaming | 1230028 | t | 108199382 | 412880111 + orders_streaming | 1230031 | t | 108199382 | 412880111 + lineitem_streaming | 1230029 | t | 412880112 | 412880112 + orders_streaming | 1230032 | t | 412880112 | 412880112 + lineitem_streaming | 1230044 | t | 412880113 | 2147483646 + orders_streaming | 1230046 | t | 412880113 | 2147483646 + lineitem_streaming | 1230045 | t | 2147483647 | 2147483647 + orders_streaming | 1230047 | t | 2147483647 | 2147483647 +(24 rows) + +\c - - - :worker_1_port +SET search_path to "Tenant Isolation"; +-- however, new tables are already created +SET citus.override_table_visibility TO false; +\d + List of relations + Schema | Name | Type | Owner +--------------------------------------------------------------------- + Tenant Isolation | lineitem_streaming | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230011 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230012 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230035 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230036 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230040 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230041 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230056 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230057 | table | mx_isolation_role_ent + Tenant Isolation | lineitem_streaming_1230058 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230014 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230015 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230038 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230039 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230042 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230043 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230059 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230060 | table | mx_isolation_role_ent + Tenant Isolation | orders_streaming_1230061 | table | mx_isolation_role_ent +(20 rows) + +\c - postgres - :worker_1_port +DROP EVENT TRIGGER abort_drop; +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; +-- tests for cluster health +SELECT count(*) FROM lineitem_streaming; + count +--------------------------------------------------------------------- + 22 +(1 row) + +SELECT count(*) FROM orders_streaming; + count +--------------------------------------------------------------------- + 7 +(1 row) + +SELECT + l_orderkey, + sum(l_extendedprice * (1 - l_discount)) as revenue, + o_orderdate +FROM + orders_streaming, + lineitem_streaming +WHERE + l_orderkey = o_orderkey +GROUP BY + l_orderkey, + o_orderdate +ORDER BY + revenue DESC, + o_orderdate; + l_orderkey | revenue | o_orderdate +--------------------------------------------------------------------- + 100 | 181042.2683 | 02-28-1998 + 102 | 159639.9677 | 05-09-1997 + 101 | 124074.5328 | 03-17-1996 + 103 | 119741.5469 | 06-20-1996 + 99 | 109604.3256 | 03-13-1994 + -1995148554 | 16890.6816 | 05-08-1995 + -1686493264 | 1988.7134 | 09-05-1997 +(7 rows) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 99; + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 100; + count +--------------------------------------------------------------------- + 5 +(1 row) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 101; + count +--------------------------------------------------------------------- + 3 +(1 row) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 102; + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 103; + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 99; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 100; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 101; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 102; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 103; + count +--------------------------------------------------------------------- + 1 +(1 row) + +-- test composite types with tenant isolation +set search_path to default; +\c - postgres - :worker_1_port +SET search_path to "Tenant Isolation", public, pg_catalog; +-- ... create a test HASH function. Though it is a poor hash function, +-- it is acceptable for our tests +SET citus.enable_metadata_sync TO OFF; +CREATE FUNCTION test_composite_type_hash(test_composite_type) RETURNS int +AS 'SELECT hashtext( ($1.i + $1.i2)::text);' +LANGUAGE SQL +IMMUTABLE +RETURNS NULL ON NULL INPUT; +RESET citus.enable_metadata_sync; +CREATE OPERATOR CLASS cats_op_fam_class +DEFAULT FOR TYPE test_composite_type USING HASH AS +OPERATOR 1 = (test_composite_type, test_composite_type), +FUNCTION 1 test_composite_type_hash(test_composite_type); +\c - - - :worker_2_port +SET search_path to "Tenant Isolation", public, pg_catalog; +-- ... create a test HASH function. Though it is a poor hash function, +-- it is acceptable for our tests +SET citus.enable_metadata_sync TO OFF; +CREATE FUNCTION test_composite_type_hash(test_composite_type) RETURNS int +AS 'SELECT hashtext( ($1.i + $1.i2)::text);' +LANGUAGE SQL +IMMUTABLE +RETURNS NULL ON NULL INPUT; +RESET citus.enable_metadata_sync; +CREATE OPERATOR CLASS cats_op_fam_class +DEFAULT FOR TYPE test_composite_type USING HASH AS +OPERATOR 1 = (test_composite_type, test_composite_type), +FUNCTION 1 test_composite_type_hash(test_composite_type); +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation", public, pg_catalog; +CREATE TABLE composite_table ( + composite_key test_composite_type); +SELECT create_distributed_table('composite_table', 'composite_key'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO composite_table VALUES ('(1, 2)'::test_composite_type); +INSERT INTO composite_table VALUES ('(1, 3)'::test_composite_type); +INSERT INTO composite_table VALUES ('(1, 4)'::test_composite_type); +SELECT isolate_tenant_to_new_shard('composite_table', '(1, 3)', shard_transfer_mode => 'force_logical'); +ERROR: cannot isolate tenants when using shard replication +SELECT count(*) FROM composite_table WHERE composite_key = '(1, 2)'::test_composite_type; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM composite_table WHERE composite_key = '(1, 3)'::test_composite_type; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM composite_table WHERE composite_key = '(1, 4)'::test_composite_type; + count +--------------------------------------------------------------------- + 1 +(1 row) + +DROP TABLE composite_table; +-- create foreign keys from a reference and distributed table +-- to another distributed table +SET search_path to "Tenant Isolation", public, pg_catalog; +SET citus.shard_replication_factor TO 1; +SET citus.shard_count to 8; +CREATE TABLE test_reference_table_fkey(id int PRIMARY KEY); +SELECT create_reference_table('test_reference_table_fkey'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE test_colocated_table_1(id int PRIMARY KEY, value_1 int, FOREIGN KEY(id) REFERENCES test_colocated_table_1(id)); +SELECT create_distributed_table('test_colocated_table_1', 'id', colocate_with => 'NONE'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE test_colocated_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id), FOREIGN KEY(id) REFERENCES test_colocated_table_1(id)); +SELECT create_distributed_table('test_colocated_table_2', 'id', colocate_with => 'test_colocated_table_1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE test_colocated_table_3(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id), FOREIGN KEY(id) REFERENCES test_colocated_table_1(id), FOREIGN KEY(id) REFERENCES test_colocated_table_2(id)); +SELECT create_distributed_table('test_colocated_table_3', 'id', colocate_with => 'test_colocated_table_1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE test_colocated_table_no_rep_identity(id int, value_1 int, FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id), FOREIGN KEY(id) REFERENCES test_colocated_table_1(id), FOREIGN KEY(id) REFERENCES test_colocated_table_2(id)); +SELECT create_distributed_table('test_colocated_table_no_rep_identity', 'id', colocate_with => 'test_colocated_table_1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO test_reference_table_fkey SELECT i FROM generate_series (0, 100) i; +INSERT INTO test_colocated_table_1 SELECT i, i FROM generate_series (0, 100) i; +INSERT INTO test_colocated_table_2 SELECT i, i FROM generate_series (0, 100) i; +INSERT INTO test_colocated_table_3 SELECT i, i FROM generate_series (0, 100) i; +INSERT INTO test_colocated_table_no_rep_identity SELECT i, i FROM generate_series (0, 100) i; +-- show that we donot support tenant isolation if the table has a colocated table with no replica identity and shard_transfer_mode=auto +SELECT isolate_tenant_to_new_shard('test_colocated_table_2', 1, 'CASCADE', shard_transfer_mode => 'auto'); +ERROR: cannot use logical replication to transfer shards of the relation test_colocated_table_no_rep_identity since it doesn't have a REPLICA IDENTITY or PRIMARY KEY +DETAIL: UPDATE and DELETE commands on the shard will error out during logical replication unless there is a REPLICA IDENTITY or PRIMARY KEY. +HINT: If you wish to continue without a replica identity set the shard_transfer_mode to 'force_logical' or 'block_writes'. +-- show that we can isolate it after removing the colocated table with no replica identity +DROP TABLE test_colocated_table_no_rep_identity; +SELECT isolate_tenant_to_new_shard('test_colocated_table_2', 1, 'CASCADE', shard_transfer_mode => 'auto'); + isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 1230103 +(1 row) + +SELECT count(*) FROM test_colocated_table_2; + count +--------------------------------------------------------------------- + 101 +(1 row) + +\c - postgres - :worker_1_port +-- show the foreign keys of the main table & its colocated shard on other tables +SELECT tbl.relname, fk."Constraint", fk."Definition" +FROM pg_catalog.pg_class tbl +JOIN public.table_fkeys fk on tbl.oid = fk.relid +WHERE tbl.relname like 'test_colocated_table_%' +ORDER BY 1, 2; + relname | Constraint | Definition +--------------------------------------------------------------------- + test_colocated_table_1 | test_colocated_table_1_id_fkey | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1(id) + test_colocated_table_1_1230069 | test_colocated_table_1_id_fkey_1230069 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230069(id) + test_colocated_table_1_1230071 | test_colocated_table_1_id_fkey_1230071 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230071(id) + test_colocated_table_1_1230073 | test_colocated_table_1_id_fkey_1230073 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230073(id) + test_colocated_table_1_1230099 | test_colocated_table_1_id_fkey_1230099 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230099(id) + test_colocated_table_1_1230100 | test_colocated_table_1_id_fkey_1230100 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230100(id) + test_colocated_table_1_1230101 | test_colocated_table_1_id_fkey_1230101 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230101(id) + test_colocated_table_2 | test_colocated_table_2_id_fkey | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1(id) + test_colocated_table_2 | test_colocated_table_2_value_1_fkey | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey(id) + test_colocated_table_2_1230077 | test_colocated_table_2_id_fkey_1230077 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230069(id) + test_colocated_table_2_1230077 | test_colocated_table_2_value_1_fkey_1230077 | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey_1230066(id) + test_colocated_table_2_1230079 | test_colocated_table_2_id_fkey_1230079 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230071(id) + test_colocated_table_2_1230079 | test_colocated_table_2_value_1_fkey_1230079 | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey_1230066(id) + test_colocated_table_2_1230081 | test_colocated_table_2_id_fkey_1230081 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230073(id) + test_colocated_table_2_1230081 | test_colocated_table_2_value_1_fkey_1230081 | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey_1230066(id) + test_colocated_table_2_1230102 | test_colocated_table_2_id_fkey_1230102 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230099(id) + test_colocated_table_2_1230102 | test_colocated_table_2_value_1_fkey_1230102 | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey_1230066(id) + test_colocated_table_2_1230103 | test_colocated_table_2_id_fkey_1230103 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230100(id) + test_colocated_table_2_1230103 | test_colocated_table_2_value_1_fkey_1230103 | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey_1230066(id) + test_colocated_table_2_1230104 | test_colocated_table_2_id_fkey_1230104 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230101(id) + test_colocated_table_2_1230104 | test_colocated_table_2_value_1_fkey_1230104 | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey_1230066(id) + test_colocated_table_3 | test_colocated_table_3_id_fkey | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1(id) + test_colocated_table_3 | test_colocated_table_3_id_fkey1 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_2(id) + test_colocated_table_3 | test_colocated_table_3_value_1_fkey | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey(id) + test_colocated_table_3_1230085 | test_colocated_table_3_id_fkey1_1230085 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_2_1230077(id) + test_colocated_table_3_1230085 | test_colocated_table_3_id_fkey_1230085 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230069(id) + test_colocated_table_3_1230085 | test_colocated_table_3_value_1_fkey_1230085 | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey_1230066(id) + test_colocated_table_3_1230087 | test_colocated_table_3_id_fkey1_1230087 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_2_1230079(id) + test_colocated_table_3_1230087 | test_colocated_table_3_id_fkey_1230087 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230071(id) + test_colocated_table_3_1230087 | test_colocated_table_3_value_1_fkey_1230087 | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey_1230066(id) + test_colocated_table_3_1230089 | test_colocated_table_3_id_fkey1_1230089 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_2_1230081(id) + test_colocated_table_3_1230089 | test_colocated_table_3_id_fkey_1230089 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230073(id) + test_colocated_table_3_1230089 | test_colocated_table_3_value_1_fkey_1230089 | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey_1230066(id) + test_colocated_table_3_1230105 | test_colocated_table_3_id_fkey1_1230105 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_2_1230102(id) + test_colocated_table_3_1230105 | test_colocated_table_3_id_fkey_1230105 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230099(id) + test_colocated_table_3_1230105 | test_colocated_table_3_value_1_fkey_1230105 | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey_1230066(id) + test_colocated_table_3_1230106 | test_colocated_table_3_id_fkey1_1230106 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_2_1230103(id) + test_colocated_table_3_1230106 | test_colocated_table_3_id_fkey_1230106 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230100(id) + test_colocated_table_3_1230106 | test_colocated_table_3_value_1_fkey_1230106 | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey_1230066(id) + test_colocated_table_3_1230107 | test_colocated_table_3_id_fkey1_1230107 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_2_1230104(id) + test_colocated_table_3_1230107 | test_colocated_table_3_id_fkey_1230107 | FOREIGN KEY (id) REFERENCES "Tenant Isolation".test_colocated_table_1_1230101(id) + test_colocated_table_3_1230107 | test_colocated_table_3_value_1_fkey_1230107 | FOREIGN KEY (value_1) REFERENCES "Tenant Isolation".test_reference_table_fkey_1230066(id) +(42 rows) + +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; +-- +-- Make sure that isolate_tenant_to_new_shard() replicats reference tables +-- when replicate_reference_tables_on_activate is off. +-- +CREATE TABLE ref_table(a int); +SELECT create_reference_table('ref_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +\c - postgres - :master_port +SET search_path to "Tenant Isolation"; +-- partitioning tests +-- create partitioned table +CREATE TABLE partitioning_test(id int, time date) PARTITION BY RANGE (time); +-- create a regular partition +CREATE TABLE partitioning_test_2009 PARTITION OF partitioning_test FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); +-- create a columnar partition +CREATE TABLE partitioning_test_2010 PARTITION OF partitioning_test FOR VALUES FROM ('2010-01-01') TO ('2011-01-01') USING columnar; +-- load some data and distribute tables +INSERT INTO partitioning_test VALUES (1, '2009-06-06'); +INSERT INTO partitioning_test VALUES (2, '2010-07-07'); +INSERT INTO partitioning_test_2009 VALUES (3, '2009-09-09'); +INSERT INTO partitioning_test_2010 VALUES (4, '2010-03-03'); +-- distribute partitioned table +SET citus.shard_replication_factor TO 1; +SELECT create_distributed_table('partitioning_test', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$"Tenant Isolation".partitioning_test_2009$$) +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$"Tenant Isolation".partitioning_test_2010$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT count(*) FROM pg_dist_shard WHERE logicalrelid = 'partitioning_test'::regclass; + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT count(*) FROM partitioning_test; + count +--------------------------------------------------------------------- + 4 +(1 row) + +-- isolate a value into its own shard +SELECT 1 FROM isolate_tenant_to_new_shard('partitioning_test', 2, 'CASCADE', shard_transfer_mode => 'force_logical'); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM pg_dist_shard WHERE logicalrelid = 'partitioning_test'::regclass; + count +--------------------------------------------------------------------- + 6 +(1 row) + +SELECT count(*) FROM partitioning_test; + count +--------------------------------------------------------------------- + 4 +(1 row) + +SET citus.replicate_reference_tables_on_activate TO off; +SET client_min_messages TO WARNING; +SELECT 1 FROM master_add_node('localhost', :master_port, groupId=>0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid = 'ref_table'::regclass; + count +--------------------------------------------------------------------- + 2 +(1 row) + +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; +SELECT 1 FROM isolate_tenant_to_new_shard('test_colocated_table_2', 2, 'CASCADE', shard_transfer_mode => 'force_logical'); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid = 'ref_table'::regclass; + count +--------------------------------------------------------------------- + 3 +(1 row) + +\c - postgres - :master_port +SELECT 1 FROM master_remove_node('localhost', :master_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SET client_min_messages TO WARNING; +DROP SCHEMA "Tenant Isolation" CASCADE; +REVOKE ALL ON SCHEMA public FROM mx_isolation_role_ent; +DROP ROLE mx_isolation_role_ent; +-- stop & resync and stop syncing metadata +SELECT stop_metadata_sync_to_node('localhost', :worker_1_port); + stop_metadata_sync_to_node +--------------------------------------------------------------------- + +(1 row) + +SELECT start_metadata_sync_to_node('localhost', :worker_1_port); + start_metadata_sync_to_node +--------------------------------------------------------------------- + +(1 row) + +SELECT stop_metadata_sync_to_node('localhost', :worker_1_port); + stop_metadata_sync_to_node +--------------------------------------------------------------------- + +(1 row) + +-- restart metadata sync for rest of the tests +SELECT start_metadata_sync_to_node('localhost', :worker_1_port); + start_metadata_sync_to_node +--------------------------------------------------------------------- + +(1 row) + +-- make sure there are no tables with non-zero colocationid +SELECT count(*) FROM pg_catalog.pg_dist_partition WHERE colocationid > 0; + count +--------------------------------------------------------------------- + 0 +(1 row) + +TRUNCATE TABLE pg_catalog.pg_dist_colocation; +ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 100; +ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART :last_placement_id; diff --git a/src/test/regress/expected/single_node_enterprise.out b/src/test/regress/expected/single_node_enterprise.out index e59565f4b..f22f85b3d 100644 --- a/src/test/regress/expected/single_node_enterprise.out +++ b/src/test/regress/expected/single_node_enterprise.out @@ -207,7 +207,7 @@ SELECT * FROM view_created_before_shard_moves; (1 row) -- show that tenant isolation works fine -SELECT isolate_tenant_to_new_shard('test', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('test', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); isolate_tenant_to_new_shard --------------------------------------------------------------------- 90731505 diff --git a/src/test/regress/expected/upgrade_list_citus_objects.out b/src/test/regress/expected/upgrade_list_citus_objects.out index bcaff941f..303326b76 100644 --- a/src/test/regress/expected/upgrade_list_citus_objects.out +++ b/src/test/regress/expected/upgrade_list_citus_objects.out @@ -144,7 +144,7 @@ ORDER BY 1; function get_rebalance_progress() function get_rebalance_table_shards_plan(regclass,real,integer,bigint[],boolean,name,real) function get_shard_id_for_distribution_column(regclass,"any") - function isolate_tenant_to_new_shard(regclass,"any",text) + function isolate_tenant_to_new_shard(regclass,"any",text,citus.shard_transfer_mode) function json_cat_agg(json) function jsonb_cat_agg(jsonb) function lock_relation_if_exists(text,text) diff --git a/src/test/regress/spec/isolation_tenant_isolation.spec b/src/test/regress/spec/isolation_tenant_isolation.spec index d7722fb5f..6b0fcd53e 100644 --- a/src/test/regress/spec/isolation_tenant_isolation.spec +++ b/src/test/regress/spec/isolation_tenant_isolation.spec @@ -66,7 +66,7 @@ step "s1-copy" step "s1-isolate-tenant" { - SELECT isolate_tenant_to_new_shard('isolation_table', 2); + SELECT isolate_tenant_to_new_shard('isolation_table', 2, shard_transfer_mode => 'block_writes'); } step "s1-commit" @@ -83,7 +83,7 @@ step "s2-begin" step "s2-isolate-tenant" { - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); } step "s2-commit" diff --git a/src/test/regress/spec/isolation_tenant_isolation_nonblocking.spec b/src/test/regress/spec/isolation_tenant_isolation_nonblocking.spec new file mode 100644 index 000000000..a1356ed17 --- /dev/null +++ b/src/test/regress/spec/isolation_tenant_isolation_nonblocking.spec @@ -0,0 +1,142 @@ +setup +{ + SET citus.shard_count to 2; + SET citus.shard_replication_factor to 1; + SELECT setval('pg_dist_shardid_seq', + CASE WHEN nextval('pg_dist_shardid_seq') > 1599999 OR nextval('pg_dist_shardid_seq') < 1500000 + THEN 1500000 + ELSE nextval('pg_dist_shardid_seq')-2 + END); + + CREATE TABLE isolation_table (id int PRIMARY KEY, value int); + SELECT create_distributed_table('isolation_table', 'id'); +} + +teardown +{ + DROP TABLE isolation_table; +} + +session "s1" + +step "s1-begin" +{ + BEGIN; + + -- the tests are written with the logic where single shard SELECTs + -- do not to open transaction blocks + SET citus.select_opens_transaction_block TO false; +} + +// cache all placements +step "s1-load-cache" +{ + TRUNCATE isolation_table; +} + +step "s1-insert" +{ + INSERT INTO isolation_table VALUES (5, 10); +} + +step "s1-update" +{ + UPDATE isolation_table SET value = 5 WHERE id = 5; +} + +step "s1-delete" +{ + DELETE FROM isolation_table WHERE id = 5; +} + +step "s1-select" +{ + SELECT count(*) FROM isolation_table WHERE id = 5; +} + +step "s1-copy" +{ + COPY isolation_table FROM PROGRAM 'echo "1,1\n2,2\n3,3\n4,4\n5,5"' WITH CSV; +} + +step "s1-isolate-tenant" +{ + SELECT isolate_tenant_to_new_shard('isolation_table', 2, shard_transfer_mode => 'force_logical'); +} + +step "s1-commit" +{ + COMMIT; +} + +session "s2" + +step "s2-begin" +{ + BEGIN; +} + +step "s2-isolate-tenant" +{ + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'force_logical'); +} + +step "s2-commit" +{ + COMMIT; +} + +step "s2-print-cluster" +{ + -- row count per shard + SELECT + nodeport, shardid, success, result + FROM + run_command_on_placements('isolation_table', 'select count(*) from %s') + ORDER BY + nodeport, shardid; + + -- rows + SELECT id, value FROM isolation_table ORDER BY id, value; +} + +session "s3" + +// this advisory lock with (almost) random values are only used +// for testing purposes. For details, check Citus' logical replication +// source code +step "s3-acquire-advisory-lock" +{ + SELECT pg_advisory_lock(44000, 55152); +} + +step "s3-release-advisory-lock" +{ + SELECT pg_advisory_unlock(44000, 55152); +} + +// Even if we do not block shard split copy operations, we eventually have to block concurrent sessions during metadata operations (tiny amount of time) +// To prevent concurrent DML from being blocked by metadata operations, we take the advisory lock from another dummy session, and let the concurrent DML +// executes concurrently with shard split copy. Just before committing, we release the advisory lock from the dummy session to allow all the sessions to finish. +// s3 takes an advisory lock before s2 takes it (we take the same lock for shard movement only during isolation tests) => +// s1 can execute its DML command concurrently with s2 shard isolation => +// s3 releases the advisory lock so that s2 can finish the transaction + +// run tenant isolation while concurrently performing an DML and index creation +// we expect DML queries of s2 to succeed without being blocked. +permutation "s1-load-cache" "s1-insert" "s3-acquire-advisory-lock" "s1-begin" "s1-select" "s2-begin" "s2-isolate-tenant" "s1-update" "s1-commit" "s3-release-advisory-lock" "s2-commit" "s2-print-cluster" +permutation "s1-load-cache" "s1-insert" "s3-acquire-advisory-lock" "s1-begin" "s1-select" "s2-begin" "s2-isolate-tenant" "s1-delete" "s1-commit" "s3-release-advisory-lock" "s2-commit" "s2-print-cluster" +permutation "s1-load-cache" "s3-acquire-advisory-lock" "s1-begin" "s1-select" "s2-begin" "s2-isolate-tenant" "s1-insert" "s1-commit" "s3-release-advisory-lock" "s2-commit" "s2-print-cluster" +permutation "s1-load-cache" "s3-acquire-advisory-lock" "s1-begin" "s1-select" "s2-begin" "s2-isolate-tenant" "s1-copy" "s1-commit" "s3-release-advisory-lock" "s2-commit" "s2-print-cluster" + +// the same tests without loading the cache at first +permutation "s1-insert" "s3-acquire-advisory-lock" "s1-begin" "s1-select" "s2-begin" "s2-isolate-tenant" "s1-update" "s1-commit" "s3-release-advisory-lock" "s2-commit" "s2-print-cluster" +permutation "s1-insert" "s3-acquire-advisory-lock" "s1-begin" "s1-select" "s2-begin" "s2-isolate-tenant" "s1-delete" "s1-commit" "s3-release-advisory-lock" "s2-commit" "s2-print-cluster" +permutation "s3-acquire-advisory-lock" "s1-begin" "s1-select" "s2-begin" "s2-isolate-tenant" "s1-insert" "s1-commit" "s3-release-advisory-lock" "s2-commit" "s2-print-cluster" +permutation "s3-acquire-advisory-lock" "s1-begin" "s1-select" "s2-begin" "s2-isolate-tenant" "s1-copy" "s1-commit" "s3-release-advisory-lock" "s2-commit" "s2-print-cluster" + +// concurrent tenant isolation blocks on different shards of the same table (or any colocated table) +permutation "s1-load-cache" "s1-insert" "s3-acquire-advisory-lock" "s1-begin" "s1-isolate-tenant" "s2-isolate-tenant" "s3-release-advisory-lock" "s1-commit" "s2-print-cluster" + +// the same test above without loading the cache at first +permutation "s1-insert" "s3-acquire-advisory-lock" "s1-begin" "s1-isolate-tenant" "s2-isolate-tenant" "s3-release-advisory-lock" "s1-commit" "s2-print-cluster" diff --git a/src/test/regress/spec/isolation_tenant_isolation_with_fkey_to_reference.spec b/src/test/regress/spec/isolation_tenant_isolation_with_fkey_to_reference.spec index d30859343..78be85198 100644 --- a/src/test/regress/spec/isolation_tenant_isolation_with_fkey_to_reference.spec +++ b/src/test/regress/spec/isolation_tenant_isolation_with_fkey_to_reference.spec @@ -69,7 +69,7 @@ step "s2-begin" step "s2-isolate-tenant" { - SELECT isolate_tenant_to_new_shard('isolation_table', 5); + SELECT isolate_tenant_to_new_shard('isolation_table', 5, shard_transfer_mode => 'block_writes'); } step "s2-add-fkey" diff --git a/src/test/regress/sql/alter_distributed_table.sql b/src/test/regress/sql/alter_distributed_table.sql index 96da74504..bfedf83b4 100644 --- a/src/test/regress/sql/alter_distributed_table.sql +++ b/src/test/regress/sql/alter_distributed_table.sql @@ -201,7 +201,7 @@ SET citus.shard_count TO 23; CREATE TABLE shard_split_table (a int, b int); SELECT create_distributed_table ('shard_split_table', 'a'); -SELECT 1 FROM isolate_tenant_to_new_shard('shard_split_table', 5); +SELECT 1 FROM isolate_tenant_to_new_shard('shard_split_table', 5, shard_transfer_mode => 'block_writes'); -- show the difference in pg_dist_colocation and citus_tables shard counts SELECT diff --git a/src/test/regress/sql/citus_local_tables_ent.sql b/src/test/regress/sql/citus_local_tables_ent.sql index 15c4371ac..c33abe09a 100644 --- a/src/test/regress/sql/citus_local_tables_ent.sql +++ b/src/test/regress/sql/citus_local_tables_ent.sql @@ -17,7 +17,7 @@ CREATE TABLE citus_local_table (a int, b int); SELECT citus_add_local_table_to_metadata('citus_local_table'); -- isolate_tenant_to_new_shard is not supported -SELECT isolate_tenant_to_new_shard('citus_local_table', 100); +SELECT isolate_tenant_to_new_shard('citus_local_table', 100, shard_transfer_mode => 'block_writes'); -- master_copy_shard_placement is not supported SELECT master_copy_shard_placement(shardid, 'localhost', :master_port, 'localhost', :worker_1_port, false) diff --git a/src/test/regress/sql/citus_non_blocking_split_columnar.sql b/src/test/regress/sql/citus_non_blocking_split_columnar.sql index 7d65997a3..b8f4084cf 100644 --- a/src/test/regress/sql/citus_non_blocking_split_columnar.sql +++ b/src/test/regress/sql/citus_non_blocking_split_columnar.sql @@ -232,7 +232,7 @@ SET citus.shard_replication_factor TO 1; 8999002, -- sensors_old ARRAY['-2127770000'], ARRAY[:worker_1_node, :worker_2_node], - 'auto'); + 'force_logical'); -- END: Split a partition table directly -- BEGIN: Validate Shard Info and Data diff --git a/src/test/regress/sql/citus_non_blocking_split_shards.sql b/src/test/regress/sql/citus_non_blocking_split_shards.sql index d6569debf..fd08068dc 100644 --- a/src/test/regress/sql/citus_non_blocking_split_shards.sql +++ b/src/test/regress/sql/citus_non_blocking_split_shards.sql @@ -9,6 +9,10 @@ Here is a high level overview of test plan: 6. Trigger Split on both shards of 'sensors'. This will also split co-located tables. 7. Move one of the split shard to test Split -> ShardMove. 8. Split an already split shard second time on a different schema. + 9. Create a colocated table with no replica identity. + 10. Show we do not allow Split with the shard transfer mode 'auto' if any colocated table has no replica identity. + 11. Drop the colocated table with no replica identity. + 12. Show we allow Split with the shard transfer mode 'auto' if all colocated tables has replica identity. */ CREATE SCHEMA "citus_split_test_schema"; @@ -228,6 +232,49 @@ SELECT shard.shardid, logicalrelid, shardminvalue, shardmaxvalue, nodename, node ORDER BY logicalrelid, shardminvalue::BIGINT; -- END: Split second time on another schema +-- BEGIN: Create a co-located table with no replica identity. +CREATE TABLE table_no_rep_id (measureid integer); +SELECT create_distributed_table('table_no_rep_id', 'measureid', colocate_with:='sensors'); +-- END: Create a co-located table with no replica identity. + +-- BEGIN: Split a shard with shard_transfer_mode='auto' and with a colocated table with no replica identity +SET citus.next_shard_id TO 8981041; +SELECT pg_catalog.citus_split_shard_by_split_points( + 8981031, + ARRAY['-2120000000'], + ARRAY[:worker_1_node, :worker_2_node], + 'auto'); + +SELECT shard.shardid, logicalrelid, shardminvalue, shardmaxvalue, nodename, nodeport + FROM pg_dist_shard AS shard + INNER JOIN pg_dist_placement placement ON shard.shardid = placement.shardid + INNER JOIN pg_dist_node node ON placement.groupid = node.groupid + INNER JOIN pg_catalog.pg_class cls ON shard.logicalrelid = cls.oid + WHERE node.noderole = 'primary' AND (logicalrelid = 'sensors'::regclass OR logicalrelid = 'colocated_dist_table'::regclass OR logicalrelid = 'table_with_index_rep_identity'::regclass) + ORDER BY logicalrelid, shardminvalue::BIGINT; +-- END: Split a shard with shard_transfer_mode='auto' and with a colocated table with no replica identity + +-- BEGIN: Drop the co-located table with no replica identity. +DROP TABLE table_no_rep_id; +-- END: Drop the co-located table with no replica identity. + +-- BEGIN: Split a shard with shard_transfer_mode='auto' and with all colocated tables has replica identity +SET citus.next_shard_id TO 8981041; +SELECT pg_catalog.citus_split_shard_by_split_points( + 8981031, + ARRAY['-2120000000'], + ARRAY[:worker_1_node, :worker_2_node], + 'auto'); + +SELECT shard.shardid, logicalrelid, shardminvalue, shardmaxvalue, nodename, nodeport + FROM pg_dist_shard AS shard + INNER JOIN pg_dist_placement placement ON shard.shardid = placement.shardid + INNER JOIN pg_dist_node node ON placement.groupid = node.groupid + INNER JOIN pg_catalog.pg_class cls ON shard.logicalrelid = cls.oid + WHERE node.noderole = 'primary' AND (logicalrelid = 'sensors'::regclass OR logicalrelid = 'colocated_dist_table'::regclass OR logicalrelid = 'table_with_index_rep_identity'::regclass) + ORDER BY logicalrelid, shardminvalue::BIGINT; +-- END: Split a shard with shard_transfer_mode='auto' and with all colocated tables has replica identity + -- BEGIN: Validate Data Count SELECT COUNT(*) FROM sensors; SELECT COUNT(*) FROM reference_table; diff --git a/src/test/regress/sql/citus_split_shard_by_split_points_negative.sql b/src/test/regress/sql/citus_split_shard_by_split_points_negative.sql index ebcd79c5e..1406615f4 100644 --- a/src/test/regress/sql/citus_split_shard_by_split_points_negative.sql +++ b/src/test/regress/sql/citus_split_shard_by_split_points_negative.sql @@ -92,7 +92,7 @@ FROM shard_ranges; -- UDF fails where source shard cannot be split further i.e min and max range is equal. -- Create a Shard where range cannot be split further -SELECT isolate_tenant_to_new_shard('table_to_split', 1); +SELECT isolate_tenant_to_new_shard('table_to_split', 1, shard_transfer_mode => 'block_writes'); SELECT citus_split_shard_by_split_points( 49761305, ARRAY['-1073741826'], diff --git a/src/test/regress/sql/failure_tenant_isolation.sql b/src/test/regress/sql/failure_tenant_isolation.sql index 5efc18290..00a75bc58 100644 --- a/src/test/regress/sql/failure_tenant_isolation.sql +++ b/src/test/regress/sql/failure_tenant_isolation.sql @@ -39,119 +39,119 @@ SELECT * FROM shard_sizes ORDER BY 1; -- failure on colocated table creation SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_2").kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- cancellation on colocated table creation SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_2").cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on colocated table population SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(302").kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- cancellation on colocated table population SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(302").cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on colocated table constraints SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_2 ADD CONSTRAINT").after(1).kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- cancellation on colocated table constraints SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_2 ADD CONSTRAINT").after(2).cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on table creation SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_1").kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- cancellation on table creation SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_1").cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on table population SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(300").kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- cancellation on table population SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(300").cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on table constraints SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_1 ADD CONSTRAINT").after(1).kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- cancellation on table constraints SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_1 ADD CONSTRAINT").after(2).cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on dropping old colocated shard SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_2").kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- cancellation on dropping old colocated shard SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_2").cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on dropping old shard SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_1").kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- cancellation on dropping old shard SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_1").cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on foreign key creation SELECT citus.mitmproxy('conn.onQuery(query="ADD CONSTRAINT table_2_ref_id_fkey FOREIGN KEY").kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on foreign key creation SELECT citus.mitmproxy('conn.onQuery(query="ADD CONSTRAINT table_2_ref_id_fkey FOREIGN KEY").after(2).cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on shard split transaction SELECT citus.mitmproxy('conn.onQuery(query="BEGIN").kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on shard split transaction SELECT citus.mitmproxy('conn.onQuery(query="BEGIN").cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on shard split transaction commit SELECT citus.mitmproxy('conn.onQuery(query="COMMIT").kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on shard split transaction commit SELECT citus.mitmproxy('conn.onQuery(query="COMMIT").cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on transaction for dropping old tables SELECT citus.mitmproxy('conn.after(1).onQuery(query="BEGIN").kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on transaction for dropping old tables SELECT citus.mitmproxy('conn.after(1).onQuery(query="BEGIN").cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on transaction for foreign key creation SELECT citus.mitmproxy('conn.after(2).onQuery(query="BEGIN").kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on transaction for foreign key creation SELECT citus.mitmproxy('conn.after(2).onQuery(query="BEGIN").cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on transaction commit for foreign key creation SELECT citus.mitmproxy('conn.after(1).onQuery(query="COMMIT").kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on transaction commit for foreign key creation SELECT citus.mitmproxy('conn.after(1).onQuery(query="COMMIT").cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on transaction prepare for dropping old tables SELECT citus.mitmproxy('conn.onQuery(query="PREPARE TRANSACTION").kill()'); @@ -162,7 +162,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="PREPARE TRANSACTION").kill()'); DO LANGUAGE plpgsql $$ BEGIN - SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); + SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); EXCEPTION WHEN OTHERS THEN RAISE 'Command failed to execute'; END; @@ -171,15 +171,15 @@ $$; -- failure on transaction prepare for dropping old tables SELECT citus.mitmproxy('conn.onQuery(query="PREPARE TRANSACTION").cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on transaction commit for dropping old tables SELECT citus.mitmproxy('conn.after(2).onQuery(query="COMMIT").kill()'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- failure on transaction commit for dropping old tables SELECT citus.mitmproxy('conn.after(2).onQuery(query="COMMIT").cancel(' || :pid || ')'); -SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- verify that the tenant is not isolated @@ -191,7 +191,7 @@ SELECT citus.mitmproxy('conn.allow()'); -- shard sizes after successful tenant isolation CREATE TABLE old_shards AS SELECT shardid FROM pg_dist_shard; WITH new_shard AS ( - SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE') AS shardid + SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode => 'block_writes') AS shardid ) SELECT row_count FROM shard_sizes diff --git a/src/test/regress/sql/failure_tenant_isolation_nonblocking.sql b/src/test/regress/sql/failure_tenant_isolation_nonblocking.sql new file mode 100644 index 000000000..834c25176 --- /dev/null +++ b/src/test/regress/sql/failure_tenant_isolation_nonblocking.sql @@ -0,0 +1,243 @@ +-- +-- failure_tenant_isolation +-- + +-- due to different libpq versions +-- some warning messages differ +-- between local and CI +SET client_min_messages TO ERROR; + +CREATE SCHEMA IF NOT EXISTS tenant_isolation; +SET SEARCH_PATH = tenant_isolation; +SET citus.shard_count TO 2; +SET citus.next_shard_id TO 300; +SET citus.shard_replication_factor TO 1; +SET citus.max_adaptive_executor_pool_size TO 1; +SELECT pg_backend_pid() as pid \gset +SELECT citus.mitmproxy('conn.allow()'); + +CREATE TABLE table_1 (id int PRIMARY KEY); +CREATE TABLE table_2 (ref_id int REFERENCES table_1(id) UNIQUE, data int); + +SELECT create_distributed_table('table_1', 'id'); +SELECT create_distributed_table('table_2', 'ref_id'); + +CREATE VIEW shard_sizes AS + SELECT shardid, result AS row_count + FROM run_command_on_placements('table_1', 'SELECT count(*) FROM %s'); + +INSERT INTO table_1 +SELECT x +FROM generate_series(1, 100) AS f (x); + +INSERT INTO table_2 +SELECT x, x +FROM generate_series(1, 100) AS f (x); + +-- initial shard sizes +SELECT * FROM shard_sizes ORDER BY 1; + +-- failure on table creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_1").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on table creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_1").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on colocated table creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_2").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on colocated table creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE tenant_isolation.table_2").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on table constraints on replica identity creation +SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_1 ADD CONSTRAINT").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on table constraints on replica identity creation +SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_1 ADD CONSTRAINT").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on publication creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE PUBLICATION").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on publication creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE PUBLICATION").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on replication slot creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE_REPLICATION_SLOT").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on replication slot creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE_REPLICATION_SLOT").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on setting snapshot +SELECT citus.mitmproxy('conn.onQuery(query="SET TRANSACTION SNAPSHOT").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on setting snapshot +SELECT citus.mitmproxy('conn.onQuery(query="SET TRANSACTION SNAPSHOT").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on table population +SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(300").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on table population +SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(300").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on colocated table population +SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(302").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on colocated table population +SELECT citus.mitmproxy('conn.onQuery(query="worker_split_copy\(302").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on replication setup udf +SELECT citus.mitmproxy('conn.onQuery(query="worker_split_shard_replication_setup").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on replication setup udf +SELECT citus.mitmproxy('conn.onQuery(query="worker_split_shard_replication_setup").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on subscription creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE SUBSCRIPTION").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on subscription creation +SELECT citus.mitmproxy('conn.onQuery(query="CREATE SUBSCRIPTION").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on colocated table constraints +SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_2 ADD CONSTRAINT").after(1).kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on colocated table constraints +SELECT citus.mitmproxy('conn.onQuery(query="ALTER TABLE tenant_isolation.table_2 ADD CONSTRAINT").after(2).cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on catching up LSN +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_current_wal_lsn").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on catching up LSN +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_current_wal_lsn").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on dropping subscription +SELECT citus.mitmproxy('conn.onQuery(query="DROP SUBSCRIPTION").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on dropping subscription +SELECT citus.mitmproxy('conn.onQuery(query="DROP SUBSCRIPTION").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on dropping publication +SELECT citus.mitmproxy('conn.onQuery(query="DROP PUBLICATION").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on dropping publication +SELECT citus.mitmproxy('conn.onQuery(query="DROP PUBLICATION").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on dropping replication slot +SELECT citus.mitmproxy('conn.onQuery(query="select pg_drop_replication_slot").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on dropping replication slot +SELECT citus.mitmproxy('conn.onQuery(query="select pg_drop_replication_slot").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on dropping old shard +SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_1").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on dropping old shard +SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_1").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on dropping old colocated shard +SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_2").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- cancellation on dropping old colocated shard +SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS tenant_isolation.table_2").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on foreign key creation +SELECT citus.mitmproxy('conn.onQuery(query="ADD CONSTRAINT table_2_ref_id_fkey FOREIGN KEY").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on foreign key creation +SELECT citus.mitmproxy('conn.onQuery(query="ADD CONSTRAINT table_2_ref_id_fkey FOREIGN KEY").after(2).cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on shard split transaction +SELECT citus.mitmproxy('conn.onQuery(query="BEGIN").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on shard split transaction +SELECT citus.mitmproxy('conn.onQuery(query="BEGIN").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on shard split transaction commit +SELECT citus.mitmproxy('conn.onQuery(query="COMMIT").kill()'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on shard split transaction commit +SELECT citus.mitmproxy('conn.onQuery(query="COMMIT").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + +-- failure on transaction prepare for dropping old tables +SELECT citus.mitmproxy('conn.onQuery(query="PREPARE TRANSACTION").kill()'); + +-- due to libpq version differences, the output might change +-- hence use code block to catch the error +\set VERBOSITY terse +DO LANGUAGE plpgsql +$$ +BEGIN + SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + EXCEPTION WHEN OTHERS THEN + RAISE 'Command failed to execute'; +END; +$$; +\set VERBOSITY default + +-- failure on transaction prepare for dropping old tables +SELECT citus.mitmproxy('conn.onQuery(query="PREPARE TRANSACTION").cancel(' || :pid || ')'); +SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical'); + + +-- verify that the tenant is not isolated +SELECT * FROM shard_sizes ORDER BY 1; + +-- Verify that tenant can be isolated after unsuccessful attempts +SELECT citus.mitmproxy('conn.allow()'); + +-- shard sizes after successful tenant isolation +CREATE TABLE old_shards AS SELECT shardid FROM pg_dist_shard; +WITH new_shard AS ( + SELECT isolate_tenant_to_new_shard('table_1', 5, 'CASCADE', shard_transfer_mode := 'force_logical') AS shardid +) +SELECT row_count +FROM shard_sizes +JOIN new_shard ON shard_sizes.shardid = new_shard.shardid; + +SELECT row_count +FROM shard_sizes +WHERE shard_sizes.shardid NOT IN (SELECT * FROM old_shards) +ORDER BY 1; + +\set VERBOSITY terse +DROP SCHEMA tenant_isolation CASCADE; +\set VERBOSITY default diff --git a/src/test/regress/sql/multi_create_table.sql b/src/test/regress/sql/multi_create_table.sql index c8a61fe93..254fb7a74 100644 --- a/src/test/regress/sql/multi_create_table.sql +++ b/src/test/regress/sql/multi_create_table.sql @@ -264,7 +264,7 @@ SET citus.shard_replication_factor TO 1; CREATE TABLE shard_split_table (a int, b int); SELECT create_distributed_table ('shard_split_table', 'a'); -SELECT 1 FROM isolate_tenant_to_new_shard('shard_split_table', 5); +SELECT 1 FROM isolate_tenant_to_new_shard('shard_split_table', 5, shard_transfer_mode => 'block_writes'); -- show the difference in pg_dist_colocation and citus_tables shard counts SELECT diff --git a/src/test/regress/sql/multi_multiuser_master_protocol.sql b/src/test/regress/sql/multi_multiuser_master_protocol.sql index 8583472cd..89e3645b4 100644 --- a/src/test/regress/sql/multi_multiuser_master_protocol.sql +++ b/src/test/regress/sql/multi_multiuser_master_protocol.sql @@ -164,7 +164,7 @@ SELECT * FROM run_command_on_placements('multiuser_schema.another_table', $$ sel ORDER BY nodename, nodeport, shardid; -- verify isolate tenant carries grants -SELECT isolate_tenant_to_new_shard('multiuser_schema.hash_table', 5); +SELECT isolate_tenant_to_new_shard('multiuser_schema.hash_table', 5, shard_transfer_mode => 'block_writes'); SELECT * FROM run_command_on_placements('multiuser_schema.hash_table', $$ select has_table_privilege('read_access', '%s', 'select') $$) ORDER BY nodename, nodeport, shardid; diff --git a/src/test/regress/sql/multi_tenant_isolation.sql b/src/test/regress/sql/multi_tenant_isolation.sql index b1a889498..3b4dd6b75 100644 --- a/src/test/regress/sql/multi_tenant_isolation.sql +++ b/src/test/regress/sql/multi_tenant_isolation.sql @@ -136,13 +136,13 @@ SELECT * FROM pg_dist_shard ORDER BY shardminvalue::BIGINT, logicalrelid; -- check without cascade option -SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, shard_transfer_mode => 'block_writes'); -- check with an input not castable to bigint -SELECT isolate_tenant_to_new_shard('lineitem_streaming', 'abc', 'CASCADE'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 'abc', 'CASCADE', shard_transfer_mode => 'block_writes'); -SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, 'CASCADE'); -SELECT isolate_tenant_to_new_shard('lineitem_streaming', 101, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, 'CASCADE', shard_transfer_mode => 'block_writes'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 101, 'CASCADE', shard_transfer_mode => 'block_writes'); -- add an explain check to see if we hit the new isolated shard EXPLAIN (COSTS false) SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 101; @@ -156,28 +156,28 @@ SET search_path to "Tenant Isolation"; -- test a failing transaction block BEGIN; -SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE'); -SELECT isolate_tenant_to_new_shard('lineitem_streaming', 102, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE', shard_transfer_mode => 'block_writes'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 102, 'CASCADE', shard_transfer_mode => 'block_writes'); COMMIT; -- test a rollback transaction block BEGIN; -SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE'); -SELECT isolate_tenant_to_new_shard('orders_streaming', 103, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE', shard_transfer_mode => 'block_writes'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 103, 'CASCADE', shard_transfer_mode => 'block_writes'); ROLLBACK; -- test a succesfull transaction block BEGIN; -SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE', shard_transfer_mode => 'block_writes'); COMMIT; -SELECT isolate_tenant_to_new_shard('orders_streaming', 103, 'CASCADE'); -SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, 'CASCADE'); -SELECT isolate_tenant_to_new_shard('orders_streaming', 101, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 103, 'CASCADE', shard_transfer_mode => 'block_writes'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, 'CASCADE', shard_transfer_mode => 'block_writes'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 101, 'CASCADE', shard_transfer_mode => 'block_writes'); -- test corner cases: hash(-1995148554) = -2147483648 and hash(-1686493264) = 2147483647 -SELECT isolate_tenant_to_new_shard('lineitem_streaming', -1995148554, 'CASCADE'); -SELECT isolate_tenant_to_new_shard('orders_streaming', -1686493264, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', -1995148554, 'CASCADE', shard_transfer_mode => 'block_writes'); +SELECT isolate_tenant_to_new_shard('orders_streaming', -1686493264, 'CASCADE', shard_transfer_mode => 'block_writes'); SELECT count(*) FROM orders_streaming WHERE o_orderkey = -1995148554; SELECT count(*) FROM orders_streaming WHERE o_orderkey = -1686493264; @@ -305,9 +305,9 @@ SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1997-07-30'; SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1998-01-15'; SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1997-08-08'; -SELECT isolate_tenant_to_new_shard('lineitem_date', '1998-05-26'); -SELECT isolate_tenant_to_new_shard('lineitem_date', '1997-07-30'); -SELECT isolate_tenant_to_new_shard('lineitem_date', '1998-01-15'); +SELECT isolate_tenant_to_new_shard('lineitem_date', '1998-05-26', shard_transfer_mode => 'block_writes'); +SELECT isolate_tenant_to_new_shard('lineitem_date', '1997-07-30', shard_transfer_mode => 'block_writes'); +SELECT isolate_tenant_to_new_shard('lineitem_date', '1998-01-15', shard_transfer_mode => 'block_writes'); SELECT count(*) FROM lineitem_date; SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1998-05-26'; @@ -320,7 +320,7 @@ SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1997-08-08'; SET search_path to "Tenant Isolation"; UPDATE pg_dist_shard_placement SET shardstate = 3 WHERE nodeport = :worker_1_port; -SELECT isolate_tenant_to_new_shard('lineitem_date', '1997-08-08'); +SELECT isolate_tenant_to_new_shard('lineitem_date', '1997-08-08', shard_transfer_mode => 'block_writes'); UPDATE pg_dist_shard_placement SET shardstate = 1 WHERE nodeport = :worker_1_port; @@ -334,7 +334,7 @@ CREATE TABLE test_append ( ); SELECT create_distributed_table('test_append', 'tenant_id', 'append'); -SELECT isolate_tenant_to_new_shard('test_append', 100); +SELECT isolate_tenant_to_new_shard('test_append', 100, shard_transfer_mode => 'block_writes'); -- check metadata for comparison SELECT * FROM pg_dist_shard @@ -366,7 +366,7 @@ SET citus.override_table_visibility TO false; SET search_path to "Tenant Isolation"; \set VERBOSITY terse -SELECT isolate_tenant_to_new_shard('orders_streaming', 104, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 104, 'CASCADE', shard_transfer_mode => 'block_writes'); \set VERBOSITY default @@ -397,7 +397,7 @@ CREATE EVENT TRIGGER abort_drop ON sql_drop SET search_path to "Tenant Isolation"; \set VERBOSITY terse -SELECT isolate_tenant_to_new_shard('orders_streaming', 104, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 104, 'CASCADE', shard_transfer_mode => 'block_writes'); \set VERBOSITY default @@ -502,7 +502,7 @@ INSERT INTO composite_table VALUES ('(1, 2)'::test_composite_type); INSERT INTO composite_table VALUES ('(1, 3)'::test_composite_type); INSERT INTO composite_table VALUES ('(1, 4)'::test_composite_type); -SELECT isolate_tenant_to_new_shard('composite_table', '(1, 3)'); +SELECT isolate_tenant_to_new_shard('composite_table', '(1, 3)', shard_transfer_mode => 'block_writes'); SELECT count(*) FROM composite_table WHERE composite_key = '(1, 2)'::test_composite_type; SELECT count(*) FROM composite_table WHERE composite_key = '(1, 3)'::test_composite_type; @@ -532,7 +532,7 @@ INSERT INTO test_colocated_table_1 SELECT i, i FROM generate_series (0, 100) i; INSERT INTO test_colocated_table_2 SELECT i, i FROM generate_series (0, 100) i; INSERT INTO test_colocated_table_3 SELECT i, i FROM generate_series (0, 100) i; -SELECT isolate_tenant_to_new_shard('test_colocated_table_2', 1, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('test_colocated_table_2', 1, 'CASCADE', shard_transfer_mode => 'block_writes'); SELECT count(*) FROM test_colocated_table_2; @@ -585,7 +585,7 @@ SELECT count(*) FROM pg_dist_shard WHERE logicalrelid = 'partitioning_test'::reg SELECT count(*) FROM partitioning_test; -- isolate a value into its own shard -SELECT 1 FROM isolate_tenant_to_new_shard('partitioning_test', 2, 'CASCADE'); +SELECT 1 FROM isolate_tenant_to_new_shard('partitioning_test', 2, 'CASCADE', shard_transfer_mode => 'block_writes'); SELECT count(*) FROM pg_dist_shard WHERE logicalrelid = 'partitioning_test'::regclass; SELECT count(*) FROM partitioning_test; @@ -601,7 +601,7 @@ SELECT count(*) FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE lo \c - mx_isolation_role_ent - :master_port SET search_path to "Tenant Isolation"; -SELECT 1 FROM isolate_tenant_to_new_shard('test_colocated_table_2', 2, 'CASCADE'); +SELECT 1 FROM isolate_tenant_to_new_shard('test_colocated_table_2', 2, 'CASCADE', shard_transfer_mode => 'block_writes'); SELECT count(*) FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid = 'ref_table'::regclass; @@ -609,6 +609,8 @@ SELECT count(*) FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE lo SELECT 1 FROM master_remove_node('localhost', :master_port); SET client_min_messages TO WARNING; DROP SCHEMA "Tenant Isolation" CASCADE; +REVOKE ALL ON SCHEMA public FROM mx_isolation_role_ent; +DROP ROLE mx_isolation_role_ent; -- stop & resync and stop syncing metadata SELECT stop_metadata_sync_to_node('localhost', :worker_1_port); diff --git a/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql b/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql new file mode 100644 index 000000000..b35c114eb --- /dev/null +++ b/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql @@ -0,0 +1,637 @@ +-- +-- MULTI_TENANT_ISOLATION +-- +-- Tests tenant isolation feature +-- +ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 1230000; + +SELECT nextval('pg_catalog.pg_dist_placement_placementid_seq') AS last_placement_id +\gset +ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART 100000; + + +CREATE SCHEMA "Tenant Isolation"; +SET search_path to "Tenant Isolation"; + +CREATE ROLE mx_isolation_role_ent WITH LOGIN; +GRANT ALL ON SCHEMA "Tenant Isolation", public TO mx_isolation_role_ent; + +-- connect with this new role +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; + +SET citus.shard_replication_factor TO 1; +SET citus.shard_count to 2; + +CREATE TABLE lineitem_streaming ( + l_orderkey bigint not null, + l_partkey integer not null, + l_suppkey integer not null, + l_linenumber integer not null, + l_quantity decimal(15, 2) not null, + l_extendedprice decimal(15, 2) not null, + l_discount decimal(15, 2) not null, + l_tax decimal(15, 2) not null, + l_returnflag char(1) not null, + l_linestatus char(1) not null, + l_shipdate date not null, + l_commitdate date not null, + l_receiptdate date not null, + l_shipinstruct char(25) not null, + l_shipmode char(10) not null, + l_comment varchar(44) not null); +SELECT create_distributed_table('lineitem_streaming', 'l_orderkey'); + +CREATE TABLE orders_streaming ( + o_orderkey bigint not null primary key, + o_custkey integer not null, + o_orderstatus char(1) not null, + o_totalprice decimal(15,2) not null, + o_orderdate date not null, + o_orderpriority char(15) not null, + o_clerk char(15) not null, + o_shippriority integer not null, + o_comment varchar(79) not null); +SELECT create_distributed_table('orders_streaming', 'o_orderkey'); + +\COPY lineitem_streaming FROM STDIN WITH DELIMITER '|' +99|87114|4639|1|10|11011.10|0.02|0.01|A|F|1994-05-18|1994-06-03|1994-05-23|COLLECT COD|RAIL|kages. requ +99|123766|3767|2|5|8948.80|0.02|0.07|R|F|1994-05-06|1994-05-28|1994-05-20|TAKE BACK RETURN|RAIL|ests cajole fluffily waters. blithe +99|134082|1622|3|42|46875.36|0.02|0.02|A|F|1994-04-19|1994-05-18|1994-04-20|NONE|RAIL|kages are fluffily furiously ir +99|108338|849|4|36|48467.88|0.09|0.02|A|F|1994-07-04|1994-04-17|1994-07-30|DELIVER IN PERSON|AIR|slyly. slyly e +100|62029|2030|1|28|27748.56|0.04|0.05|N|O|1998-05-08|1998-05-13|1998-06-07|COLLECT COD|TRUCK|sts haggle. slowl +100|115979|8491|2|22|43889.34|0.00|0.07|N|O|1998-06-24|1998-04-12|1998-06-29|DELIVER IN PERSON|SHIP|nto beans alongside of the fi +100|46150|8655|3|46|50422.90|0.03|0.04|N|O|1998-05-02|1998-04-10|1998-05-22|TAKE BACK RETURN|SHIP|ular accounts. even +100|38024|3031|4|14|13468.28|0.06|0.03|N|O|1998-05-22|1998-05-01|1998-06-03|COLLECT COD|MAIL|y. furiously ironic ideas gr +100|53439|955|5|37|51519.91|0.05|0.00|N|O|1998-03-06|1998-04-16|1998-03-31|TAKE BACK RETURN|TRUCK|nd the quickly s +101|118282|5816|1|49|63713.72|0.10|0.00|N|O|1996-06-21|1996-05-27|1996-06-29|DELIVER IN PERSON|REG AIR|ts +101|163334|883|2|36|50303.88|0.00|0.01|N|O|1996-05-19|1996-05-01|1996-06-04|DELIVER IN PERSON|AIR|tes. blithely pending dolphins x-ray f +101|138418|5958|3|12|17476.92|0.06|0.02|N|O|1996-03-29|1996-04-20|1996-04-12|COLLECT COD|MAIL|. quickly regular +102|88914|3931|1|37|70407.67|0.06|0.00|N|O|1997-07-24|1997-08-02|1997-08-07|TAKE BACK RETURN|SHIP|ully across the ideas. final deposit +102|169238|6787|2|34|44445.82|0.03|0.08|N|O|1997-08-09|1997-07-28|1997-08-26|TAKE BACK RETURN|SHIP|eposits cajole across +102|182321|4840|3|25|35083.00|0.01|0.01|N|O|1997-07-31|1997-07-24|1997-08-17|NONE|RAIL|bits. ironic accoun +102|61158|8677|4|15|16787.25|0.07|0.07|N|O|1997-06-02|1997-07-13|1997-06-04|DELIVER IN PERSON|SHIP|final packages. carefully even excu +103|194658|2216|1|6|10515.90|0.03|0.05|N|O|1996-10-11|1996-07-25|1996-10-28|NONE|FOB|cajole. carefully ex +103|10426|2928|2|37|49447.54|0.02|0.07|N|O|1996-09-17|1996-07-27|1996-09-20|TAKE BACK RETURN|MAIL|ies. quickly ironic requests use blithely +103|28431|8432|3|23|31266.89|0.01|0.04|N|O|1996-09-11|1996-09-18|1996-09-26|NONE|FOB|ironic accou +103|29022|4027|4|32|30432.64|0.01|0.07|N|O|1996-07-30|1996-08-06|1996-08-04|NONE|RAIL|kages doze. special, regular deposit +-1995148554|112942|2943|1|9|17594.46|0.04|0.04|N|O|1996-08-03|1996-05-31|1996-08-04|DELIVER IN PERSON|TRUCK|c realms print carefully car +-1686493264|15110|113|5|2|2050.22|0.03|0.08|R|F|1994-04-26|1994-03-15|1994-05-15|TAKE BACK RETURN|MAIL|e final, regular requests. carefully +\. + +\COPY orders_streaming FROM STDIN WITH DELIMITER '|' +99|890|F|108594.87|1994-03-13|4-NOT SPECIFIED|Clerk#000000973|0|e carefully ironic packages. pending +100|1471|O|198978.27|1998-02-28|4-NOT SPECIFIED|Clerk#000000577|0|heodolites detect slyly alongside of the ent +101|280|O|118448.39|1996-03-17|3-MEDIUM|Clerk#000000419|0|ding accounts above the slyly final asymptote +102|8|O|184806.58|1997-05-09|2-HIGH|Clerk#000000596|0| slyly according to the asymptotes. carefully final packages integrate furious +103|292|O|118745.16|1996-06-20|4-NOT SPECIFIED|Clerk#000000090|0|ges. carefully unusual instructions haggle quickly regular f +-1995148554|142|O|3553.15|1995-05-08|3-MEDIUM|Clerk#000000378|0|nts hinder fluffily ironic instructions. express, express excuses +-1686493264|878|O|177809.13|1997-09-05|3-MEDIUM|Clerk#000000379|0|y final packages. final foxes since the quickly even +\. + +ALTER TABLE lineitem_streaming ADD CONSTRAINT test_constraint + FOREIGN KEY(l_orderkey) REFERENCES orders_streaming(o_orderkey); + +-- test failing foreign constraints +\COPY lineitem_streaming FROM STDIN WITH DELIMITER '|' +128|106828|9339|1|38|69723.16|0.06|0.01|A|F|1992-09-01|1992-08-27|1992-10-01|TAKE BACK RETURN|FOB| cajole careful +\. + +-- tests for cluster health +SELECT count(*) FROM lineitem_streaming; +SELECT count(*) FROM orders_streaming; + +SELECT + l_orderkey, + sum(l_extendedprice * (1 - l_discount)) as revenue, + o_orderdate +FROM + orders_streaming, + lineitem_streaming +WHERE + l_orderkey = o_orderkey +GROUP BY + l_orderkey, + o_orderdate +ORDER BY + revenue DESC, + o_orderdate; + +-- Checks to see if metadata and data are isolated properly. If there are problems in +-- metadata and/or data on workers, these queries should return different results below +-- after tenant isolation operations are applied. +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 99; +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 100; +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 101; +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 102; +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 103; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 99; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 100; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 101; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 102; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 103; + +SELECT * FROM pg_dist_shard + WHERE logicalrelid = 'lineitem_streaming'::regclass OR logicalrelid = 'orders_streaming'::regclass + ORDER BY shardminvalue::BIGINT, logicalrelid; + +-- check without cascade option +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, shard_transfer_mode => 'force_logical'); + +-- check with an input not castable to bigint +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 'abc', 'CASCADE', shard_transfer_mode => 'force_logical'); + +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, 'CASCADE', shard_transfer_mode => 'force_logical'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 101, 'CASCADE', shard_transfer_mode => 'force_logical'); + +-- add an explain check to see if we hit the new isolated shard +EXPLAIN (COSTS false) SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 101; + +-- create an MX node +\c - postgres - :master_port +SELECT start_metadata_sync_to_node('localhost', :worker_1_port); + +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; + +-- test a failing transaction block +BEGIN; +SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE', shard_transfer_mode => 'force_logical'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 102, 'CASCADE', shard_transfer_mode => 'force_logical'); +COMMIT; + +-- test a rollback transaction block +BEGIN; +SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE', shard_transfer_mode => 'force_logical'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 103, 'CASCADE', shard_transfer_mode => 'force_logical'); +ROLLBACK; + +-- test a succesfull transaction block +BEGIN; +SELECT isolate_tenant_to_new_shard('orders_streaming', 102, 'CASCADE', shard_transfer_mode => 'force_logical'); +COMMIT; + +SELECT isolate_tenant_to_new_shard('orders_streaming', 103, 'CASCADE', shard_transfer_mode => 'force_logical'); +SELECT isolate_tenant_to_new_shard('lineitem_streaming', 100, 'CASCADE', shard_transfer_mode => 'force_logical'); +SELECT isolate_tenant_to_new_shard('orders_streaming', 101, 'CASCADE', shard_transfer_mode => 'force_logical'); + +-- test corner cases: hash(-1995148554) = -2147483648 and hash(-1686493264) = 2147483647 +SELECT isolate_tenant_to_new_shard('lineitem_streaming', -1995148554, 'CASCADE', shard_transfer_mode => 'force_logical'); +SELECT isolate_tenant_to_new_shard('orders_streaming', -1686493264, 'CASCADE', shard_transfer_mode => 'force_logical'); + +SELECT count(*) FROM orders_streaming WHERE o_orderkey = -1995148554; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = -1686493264; + +-- tests for cluster health +SELECT count(*) FROM lineitem_streaming; +SELECT count(*) FROM orders_streaming; + +SELECT + l_orderkey, + sum(l_extendedprice * (1 - l_discount)) as revenue, + o_orderdate +FROM + orders_streaming, + lineitem_streaming +WHERE + l_orderkey = o_orderkey +GROUP BY + l_orderkey, + o_orderdate +ORDER BY + revenue DESC, + o_orderdate; + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 99; +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 100; +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 101; +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 102; +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 103; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 99; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 100; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 101; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 102; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 103; + +SELECT * FROM pg_dist_shard + WHERE logicalrelid = 'lineitem_streaming'::regclass OR logicalrelid = 'orders_streaming'::regclass + ORDER BY shardminvalue::BIGINT, logicalrelid; + +SELECT * FROM pg_dist_shard_placement WHERE shardid >= 1230000 ORDER BY nodeport, shardid; + +-- test failing foreign constraints after multiple tenant isolation +\COPY lineitem_streaming FROM STDIN WITH DELIMITER '|' +128|106828|9339|1|38|69723.16|0.06|0.01|A|F|1992-09-01|1992-08-27|1992-10-01|TAKE BACK RETURN|FOB| cajole careful +\. + +-- connect to the worker node with metadata +\c - mx_isolation_role_ent - :worker_1_port +SET search_path to "Tenant Isolation"; + +-- check mx tables +SELECT count(*) FROM lineitem_streaming; +SELECT count(*) FROM orders_streaming; + +SELECT + l_orderkey, + sum(l_extendedprice * (1 - l_discount)) as revenue, + o_orderdate +FROM + orders_streaming, + lineitem_streaming +WHERE + l_orderkey = o_orderkey +GROUP BY + l_orderkey, + o_orderdate +ORDER BY + revenue DESC, + o_orderdate; + +-- check shards +SET citus.override_table_visibility TO false; +\d + +\c - postgres - :worker_1_port +SET search_path to "Tenant Isolation"; +SELECT "Column", "Type", "Modifiers" FROM public.table_desc WHERE relid='orders_streaming_1230039'::regclass; + +\c - mx_isolation_role_ent - :worker_1_port +SET search_path to "Tenant Isolation"; + +-- check MX metadata +SELECT * FROM pg_dist_shard + WHERE logicalrelid = 'lineitem_streaming'::regclass OR logicalrelid = 'orders_streaming'::regclass + ORDER BY shardminvalue::BIGINT, logicalrelid; + +-- return to master node +\c - mx_isolation_role_ent - :master_port + +-- test a distribution type which does not have a sql hash function +SET search_path to "Tenant Isolation"; + +SET citus.shard_replication_factor TO 2; +SET citus.shard_count to 2; + +CREATE TABLE lineitem_date ( + l_orderkey bigint not null, + l_partkey integer not null, + l_suppkey integer not null, + l_linenumber integer not null, + l_quantity decimal(15, 2) not null, + l_extendedprice decimal(15, 2) not null, + l_discount decimal(15, 2) not null, + l_tax decimal(15, 2) not null, + l_returnflag char(1) not null, + l_linestatus char(1) not null, + l_shipdate date not null, + l_commitdate date not null, + l_receiptdate date not null, + l_shipinstruct char(25) not null, + l_shipmode char(10) not null, + l_comment varchar(44) not null); +SELECT create_distributed_table('lineitem_date', 'l_shipdate'); + +\COPY lineitem_date FROM STDIN WITH DELIMITER '|' +390|106523|9034|1|10|15295.20|0.02|0.05|N|O|1998-05-26|1998-07-06|1998-06-23|TAKE BACK RETURN|SHIP| requests. final accounts x-ray beside the +1347|112077|4589|4|28|30493.96|0.01|0.00|N|O|1997-07-30|1997-07-22|1997-08-18|TAKE BACK RETURN|FOB|foxes after the blithely special i +1794|116434|1457|5|47|68170.21|0.10|0.06|N|O|1998-01-15|1997-11-30|1998-02-14|DELIVER IN PERSON|TRUCK| haggle slyly. furiously express orbit +1859|74969|4970|1|18|34991.28|0.10|0.00|N|O|1997-08-08|1997-06-30|1997-08-26|TAKE BACK RETURN|SHIP|e carefully a +\. + +SELECT count(*) FROM lineitem_date; +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1998-05-26'; +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1997-07-30'; +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1998-01-15'; +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1997-08-08'; + +SELECT isolate_tenant_to_new_shard('lineitem_date', '1998-05-26', shard_transfer_mode => 'force_logical'); +SELECT isolate_tenant_to_new_shard('lineitem_date', '1997-07-30', shard_transfer_mode => 'force_logical'); +SELECT isolate_tenant_to_new_shard('lineitem_date', '1998-01-15', shard_transfer_mode => 'force_logical'); + +SELECT count(*) FROM lineitem_date; +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1998-05-26'; +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1997-07-30'; +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1998-01-15'; +SELECT count(*) FROM lineitem_date WHERE l_shipdate = '1997-08-08'; + +-- test with invalid shard placements +\c - postgres - :master_port +SET search_path to "Tenant Isolation"; + +UPDATE pg_dist_shard_placement SET shardstate = 3 WHERE nodeport = :worker_1_port; +SELECT isolate_tenant_to_new_shard('lineitem_date', '1997-08-08', shard_transfer_mode => 'force_logical'); + +UPDATE pg_dist_shard_placement SET shardstate = 1 WHERE nodeport = :worker_1_port; + +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; +DROP TABLE lineitem_date; + +-- test on append distributed table +CREATE TABLE test_append ( + tenant_id integer +); + +SELECT create_distributed_table('test_append', 'tenant_id', 'append'); +SELECT isolate_tenant_to_new_shard('test_append', 100, shard_transfer_mode => 'force_logical'); + +-- check metadata for comparison +SELECT * FROM pg_dist_shard + WHERE logicalrelid = 'lineitem_streaming'::regclass OR logicalrelid = 'orders_streaming'::regclass + ORDER BY shardminvalue::BIGINT, logicalrelid; + +-- test failure scenarios with triggers on workers +\c - postgres - :worker_1_port +SET search_path to "Tenant Isolation"; + +SET citus.enable_metadata_sync TO OFF; +CREATE OR REPLACE FUNCTION abort_any_command() + RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + RAISE EXCEPTION 'command % is disabled', tg_tag; +END; +$$; +RESET citus.enable_metadata_sync; + +CREATE EVENT TRIGGER abort_ddl ON ddl_command_end + EXECUTE PROCEDURE abort_any_command(); + +SET citus.override_table_visibility TO false; +\d + +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; + +\set VERBOSITY terse +SELECT isolate_tenant_to_new_shard('orders_streaming', 104, 'CASCADE', shard_transfer_mode => 'force_logical'); + +\set VERBOSITY default + +\c - postgres - :worker_1_port +SET search_path to "Tenant Isolation"; + +SET citus.override_table_visibility TO false; +\d + +DROP EVENT TRIGGER abort_ddl; + +-- create a trigger for drops +SET citus.enable_metadata_sync TO OFF; +CREATE OR REPLACE FUNCTION abort_drop_command() + RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + RAISE EXCEPTION 'command % is disabled', tg_tag; +END; +$$; +RESET citus.enable_metadata_sync; + +CREATE EVENT TRIGGER abort_drop ON sql_drop + EXECUTE PROCEDURE abort_drop_command(); + +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; + +\set VERBOSITY terse +SELECT isolate_tenant_to_new_shard('orders_streaming', 104, 'CASCADE', shard_transfer_mode => 'force_logical'); + +\set VERBOSITY default + +-- check if metadata is changed +SELECT * FROM pg_dist_shard + WHERE logicalrelid = 'lineitem_streaming'::regclass OR logicalrelid = 'orders_streaming'::regclass + ORDER BY shardminvalue::BIGINT, logicalrelid; + +\c - - - :worker_1_port +SET search_path to "Tenant Isolation"; + +-- however, new tables are already created +SET citus.override_table_visibility TO false; +\d + +\c - postgres - :worker_1_port + +DROP EVENT TRIGGER abort_drop; + +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; + +-- tests for cluster health +SELECT count(*) FROM lineitem_streaming; +SELECT count(*) FROM orders_streaming; + +SELECT + l_orderkey, + sum(l_extendedprice * (1 - l_discount)) as revenue, + o_orderdate +FROM + orders_streaming, + lineitem_streaming +WHERE + l_orderkey = o_orderkey +GROUP BY + l_orderkey, + o_orderdate +ORDER BY + revenue DESC, + o_orderdate; + +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 99; +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 100; +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 101; +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 102; +SELECT count(*) FROM lineitem_streaming WHERE l_orderkey = 103; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 99; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 100; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 101; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 102; +SELECT count(*) FROM orders_streaming WHERE o_orderkey = 103; + +-- test composite types with tenant isolation +set search_path to default; + +\c - postgres - :worker_1_port +SET search_path to "Tenant Isolation", public, pg_catalog; + +-- ... create a test HASH function. Though it is a poor hash function, +-- it is acceptable for our tests +SET citus.enable_metadata_sync TO OFF; +CREATE FUNCTION test_composite_type_hash(test_composite_type) RETURNS int +AS 'SELECT hashtext( ($1.i + $1.i2)::text);' +LANGUAGE SQL +IMMUTABLE +RETURNS NULL ON NULL INPUT; +RESET citus.enable_metadata_sync; + +CREATE OPERATOR CLASS cats_op_fam_class +DEFAULT FOR TYPE test_composite_type USING HASH AS +OPERATOR 1 = (test_composite_type, test_composite_type), +FUNCTION 1 test_composite_type_hash(test_composite_type); + +\c - - - :worker_2_port +SET search_path to "Tenant Isolation", public, pg_catalog; + +-- ... create a test HASH function. Though it is a poor hash function, +-- it is acceptable for our tests +SET citus.enable_metadata_sync TO OFF; +CREATE FUNCTION test_composite_type_hash(test_composite_type) RETURNS int +AS 'SELECT hashtext( ($1.i + $1.i2)::text);' +LANGUAGE SQL +IMMUTABLE +RETURNS NULL ON NULL INPUT; +RESET citus.enable_metadata_sync; + +CREATE OPERATOR CLASS cats_op_fam_class +DEFAULT FOR TYPE test_composite_type USING HASH AS +OPERATOR 1 = (test_composite_type, test_composite_type), +FUNCTION 1 test_composite_type_hash(test_composite_type); + +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation", public, pg_catalog; + +CREATE TABLE composite_table ( + composite_key test_composite_type); + +SELECT create_distributed_table('composite_table', 'composite_key'); + +INSERT INTO composite_table VALUES ('(1, 2)'::test_composite_type); +INSERT INTO composite_table VALUES ('(1, 3)'::test_composite_type); +INSERT INTO composite_table VALUES ('(1, 4)'::test_composite_type); + +SELECT isolate_tenant_to_new_shard('composite_table', '(1, 3)', shard_transfer_mode => 'force_logical'); + +SELECT count(*) FROM composite_table WHERE composite_key = '(1, 2)'::test_composite_type; +SELECT count(*) FROM composite_table WHERE composite_key = '(1, 3)'::test_composite_type; +SELECT count(*) FROM composite_table WHERE composite_key = '(1, 4)'::test_composite_type; + +DROP TABLE composite_table; + +-- create foreign keys from a reference and distributed table +-- to another distributed table +SET search_path to "Tenant Isolation", public, pg_catalog; +SET citus.shard_replication_factor TO 1; +SET citus.shard_count to 8; +CREATE TABLE test_reference_table_fkey(id int PRIMARY KEY); +SELECT create_reference_table('test_reference_table_fkey'); + +CREATE TABLE test_colocated_table_1(id int PRIMARY KEY, value_1 int, FOREIGN KEY(id) REFERENCES test_colocated_table_1(id)); +SELECT create_distributed_table('test_colocated_table_1', 'id', colocate_with => 'NONE'); + +CREATE TABLE test_colocated_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id), FOREIGN KEY(id) REFERENCES test_colocated_table_1(id)); +SELECT create_distributed_table('test_colocated_table_2', 'id', colocate_with => 'test_colocated_table_1'); + +CREATE TABLE test_colocated_table_3(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id), FOREIGN KEY(id) REFERENCES test_colocated_table_1(id), FOREIGN KEY(id) REFERENCES test_colocated_table_2(id)); +SELECT create_distributed_table('test_colocated_table_3', 'id', colocate_with => 'test_colocated_table_1'); + +CREATE TABLE test_colocated_table_no_rep_identity(id int, value_1 int, FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id), FOREIGN KEY(id) REFERENCES test_colocated_table_1(id), FOREIGN KEY(id) REFERENCES test_colocated_table_2(id)); +SELECT create_distributed_table('test_colocated_table_no_rep_identity', 'id', colocate_with => 'test_colocated_table_1'); + +INSERT INTO test_reference_table_fkey SELECT i FROM generate_series (0, 100) i; +INSERT INTO test_colocated_table_1 SELECT i, i FROM generate_series (0, 100) i; +INSERT INTO test_colocated_table_2 SELECT i, i FROM generate_series (0, 100) i; +INSERT INTO test_colocated_table_3 SELECT i, i FROM generate_series (0, 100) i; +INSERT INTO test_colocated_table_no_rep_identity SELECT i, i FROM generate_series (0, 100) i; + +-- show that we donot support tenant isolation if the table has a colocated table with no replica identity and shard_transfer_mode=auto +SELECT isolate_tenant_to_new_shard('test_colocated_table_2', 1, 'CASCADE', shard_transfer_mode => 'auto'); + +-- show that we can isolate it after removing the colocated table with no replica identity +DROP TABLE test_colocated_table_no_rep_identity; +SELECT isolate_tenant_to_new_shard('test_colocated_table_2', 1, 'CASCADE', shard_transfer_mode => 'auto'); + +SELECT count(*) FROM test_colocated_table_2; + +\c - postgres - :worker_1_port + +-- show the foreign keys of the main table & its colocated shard on other tables +SELECT tbl.relname, fk."Constraint", fk."Definition" +FROM pg_catalog.pg_class tbl +JOIN public.table_fkeys fk on tbl.oid = fk.relid +WHERE tbl.relname like 'test_colocated_table_%' +ORDER BY 1, 2; + +\c - mx_isolation_role_ent - :master_port + +SET search_path to "Tenant Isolation"; + +-- +-- Make sure that isolate_tenant_to_new_shard() replicats reference tables +-- when replicate_reference_tables_on_activate is off. +-- + + +CREATE TABLE ref_table(a int); +SELECT create_reference_table('ref_table'); + +\c - postgres - :master_port +SET search_path to "Tenant Isolation"; + +-- partitioning tests +-- create partitioned table +CREATE TABLE partitioning_test(id int, time date) PARTITION BY RANGE (time); + +-- create a regular partition +CREATE TABLE partitioning_test_2009 PARTITION OF partitioning_test FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); +-- create a columnar partition +CREATE TABLE partitioning_test_2010 PARTITION OF partitioning_test FOR VALUES FROM ('2010-01-01') TO ('2011-01-01') USING columnar; + +-- load some data and distribute tables +INSERT INTO partitioning_test VALUES (1, '2009-06-06'); +INSERT INTO partitioning_test VALUES (2, '2010-07-07'); + +INSERT INTO partitioning_test_2009 VALUES (3, '2009-09-09'); +INSERT INTO partitioning_test_2010 VALUES (4, '2010-03-03'); + +-- distribute partitioned table +SET citus.shard_replication_factor TO 1; +SELECT create_distributed_table('partitioning_test', 'id'); + +SELECT count(*) FROM pg_dist_shard WHERE logicalrelid = 'partitioning_test'::regclass; +SELECT count(*) FROM partitioning_test; + +-- isolate a value into its own shard +SELECT 1 FROM isolate_tenant_to_new_shard('partitioning_test', 2, 'CASCADE', shard_transfer_mode => 'force_logical'); + +SELECT count(*) FROM pg_dist_shard WHERE logicalrelid = 'partitioning_test'::regclass; +SELECT count(*) FROM partitioning_test; + + +SET citus.replicate_reference_tables_on_activate TO off; +SET client_min_messages TO WARNING; + +SELECT 1 FROM master_add_node('localhost', :master_port, groupId=>0); + +SELECT count(*) FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid = 'ref_table'::regclass; + +\c - mx_isolation_role_ent - :master_port +SET search_path to "Tenant Isolation"; + +SELECT 1 FROM isolate_tenant_to_new_shard('test_colocated_table_2', 2, 'CASCADE', shard_transfer_mode => 'force_logical'); + +SELECT count(*) FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid = 'ref_table'::regclass; + +\c - postgres - :master_port +SELECT 1 FROM master_remove_node('localhost', :master_port); +SET client_min_messages TO WARNING; +DROP SCHEMA "Tenant Isolation" CASCADE; +REVOKE ALL ON SCHEMA public FROM mx_isolation_role_ent; +DROP ROLE mx_isolation_role_ent; + +-- stop & resync and stop syncing metadata +SELECT stop_metadata_sync_to_node('localhost', :worker_1_port); +SELECT start_metadata_sync_to_node('localhost', :worker_1_port); +SELECT stop_metadata_sync_to_node('localhost', :worker_1_port); + +-- restart metadata sync for rest of the tests +SELECT start_metadata_sync_to_node('localhost', :worker_1_port); + +-- make sure there are no tables with non-zero colocationid +SELECT count(*) FROM pg_catalog.pg_dist_partition WHERE colocationid > 0; +TRUNCATE TABLE pg_catalog.pg_dist_colocation; +ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 100; + +ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART :last_placement_id; diff --git a/src/test/regress/sql/single_node_enterprise.sql b/src/test/regress/sql/single_node_enterprise.sql index 8c7764835..9ad590bb8 100644 --- a/src/test/regress/sql/single_node_enterprise.sql +++ b/src/test/regress/sql/single_node_enterprise.sql @@ -177,7 +177,7 @@ CREATE VIEW view_created_before_shard_moves AS SELECT * FROM view_created_before_shard_moves; -- show that tenant isolation works fine -SELECT isolate_tenant_to_new_shard('test', 5, 'CASCADE'); +SELECT isolate_tenant_to_new_shard('test', 5, 'CASCADE', shard_transfer_mode => 'block_writes'); -- in the first iteration, have an -- hybrid cluster meaning that