Skip to content

Commit f67448b

Browse files
committed
commit
1 parent e228834 commit f67448b

31 files changed

+920
-80
lines changed

src/backend/distributed/commands/database.c

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "distributed/metadata_utility.h"
4242
#include "distributed/multi_executor.h"
4343
#include "distributed/relation_access_tracking.h"
44+
#include "distributed/serialize_distributed_ddls.h"
4445
#include "distributed/worker_protocol.h"
4546
#include "distributed/worker_transaction.h"
4647

@@ -161,6 +162,10 @@ get_database_owner(Oid dbId)
161162
*
162163
* In this stage we can prepare the commands that need to be run on all workers to grant
163164
* on databases.
165+
*
166+
* We also serialize database commands globally by acquiring Citus specific advisory
167+
* locks on affected databases based on OCLASS_DATABASE on the first primary worker
168+
* node.
164169
*/
165170
List *
166171
PreprocessGrantOnDatabaseStmt(Node *node, const char *queryString,
@@ -183,6 +188,24 @@ PreprocessGrantOnDatabaseStmt(Node *node, const char *queryString,
183188

184189
EnsureCoordinator();
185190

191+
/*
192+
* Sort before acquring the locks to prevent deadlocks that could happen
193+
* due to acquiring the locks in different orders.
194+
*/
195+
List *sortedDistributedDatabases = SortList(distributedDatabases, CompareStringNodes);
196+
197+
String *distributedDatabaseName = NULL;
198+
foreach_ptr(distributedDatabaseName, sortedDistributedDatabases)
199+
{
200+
/* FilterDistributedDatabases ensured that the database exists and is distributed */
201+
bool missingOk = false;
202+
ObjectAddress *distributedDatabaseAddress = GetDatabaseAddressFromDatabaseName(
203+
strVal(distributedDatabaseName), missingOk);
204+
205+
SerializeDistributedDDLsOnObjectClass(OCLASS_DATABASE,
206+
distributedDatabaseAddress->objectId);
207+
}
208+
186209
List *originalObjects = stmt->objects;
187210

188211
stmt->objects = distributedDatabases;
@@ -248,6 +271,9 @@ IsSetTablespaceStatement(AlterDatabaseStmt *stmt)
248271
*
249272
* In this stage we can prepare the commands that need to be run on all workers to grant
250273
* on databases.
274+
*
275+
* We also serialize database commands globally by acquiring a Citus specific advisory
276+
* lock based on OCLASS_DATABASE on the first primary worker node.
251277
*/
252278
List *
253279
PreprocessAlterDatabaseStmt(Node *node, const char *queryString,
@@ -264,6 +290,7 @@ PreprocessAlterDatabaseStmt(Node *node, const char *queryString,
264290
}
265291

266292
EnsureCoordinator();
293+
SerializeDistributedDDLsOnObjectClass(OCLASS_DATABASE, dbAddress->objectId);
267294

268295
char *sql = DeparseTreeNode((Node *) stmt);
269296

@@ -296,6 +323,9 @@ PreprocessAlterDatabaseStmt(Node *node, const char *queryString,
296323
*
297324
* In this stage we can prepare the commands that need to be run on all workers to grant
298325
* on databases.
326+
*
327+
* We also serialize database commands globally by acquiring a Citus specific advisory
328+
* lock based on OCLASS_DATABASE on the first primary worker node.
299329
*/
300330
List *
301331
PreprocessAlterDatabaseRefreshCollStmt(Node *node, const char *queryString,
@@ -312,6 +342,7 @@ PreprocessAlterDatabaseRefreshCollStmt(Node *node, const char *queryString,
312342
}
313343

314344
EnsureCoordinator();
345+
SerializeDistributedDDLsOnObjectClass(OCLASS_DATABASE, dbAddress->objectId);
315346

316347
char *sql = DeparseTreeNode((Node *) stmt);
317348

@@ -325,8 +356,51 @@ PreprocessAlterDatabaseRefreshCollStmt(Node *node, const char *queryString,
325356

326357
#endif
327358

359+
360+
/*
361+
* PreprocessAlterDatabaseRenameStmt is executed before the statement is applied to
362+
* the local postgres instance.
363+
*
364+
* We also serialize database commands globally by acquiring a Citus specific advisory
365+
* lock based on OCLASS_DATABASE on the first primary worker node.
366+
*
367+
* We acquire this lock here instead of PostprocessAlterDatabaseRenameStmt because the
368+
* command renames the database and SerializeDistributedDDLsOnObjectClass resolves the
369+
* object on workers based on database name. For this reason, we need to acquire the lock
370+
* before the command is applied to the local postgres instance.
371+
*/
372+
List *
373+
PreprocessAlterDatabaseRenameStmt(Node *node, const char *queryString,
374+
ProcessUtilityContext processUtilityContext)
375+
{
376+
bool missingOk = true;
377+
RenameStmt *stmt = castNode(RenameStmt, node);
378+
ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->subname,
379+
missingOk);
380+
381+
if (!ShouldPropagate() || !IsAnyObjectDistributed(list_make1(dbAddress)))
382+
{
383+
return NIL;
384+
}
385+
386+
EnsureCoordinator();
387+
388+
/*
389+
* Different than other ALTER DATABASE commands, we first acquire a lock
390+
* by providing InvalidOid because we want ALTER TABLE .. RENAME TO ..
391+
* commands to block not only with ALTER DATABASE operations but also
392+
* with CREATE DATABASE operations because they might cause name conflicts
393+
* and that could also cause deadlocks too.
394+
*/
395+
SerializeDistributedDDLsOnObjectClass(OCLASS_DATABASE, InvalidOid);
396+
SerializeDistributedDDLsOnObjectClass(OCLASS_DATABASE, dbAddress->objectId);
397+
398+
return NIL;
399+
}
400+
401+
328402
/*
329-
* PreprocessAlterDatabaseRenameStmt is executed before the statement is applied to the local
403+
* PostprocessAlterDatabaseRenameStmt is executed after the statement is applied to the local
330404
* postgres instance. In this stage we prepare ALTER DATABASE RENAME statement to be run on
331405
* all workers.
332406
*/
@@ -361,6 +435,9 @@ PostprocessAlterDatabaseRenameStmt(Node *node, const char *queryString)
361435
*
362436
* In this stage we can prepare the commands that need to be run on all workers to grant
363437
* on databases.
438+
*
439+
* We also serialize database commands globally by acquiring a Citus specific advisory
440+
* lock based on OCLASS_DATABASE on the first primary worker node.
364441
*/
365442
List *
366443
PreprocessAlterDatabaseSetStmt(Node *node, const char *queryString,
@@ -377,6 +454,7 @@ PreprocessAlterDatabaseSetStmt(Node *node, const char *queryString,
377454
}
378455

379456
EnsureCoordinator();
457+
SerializeDistributedDDLsOnObjectClass(OCLASS_DATABASE, dbAddress->objectId);
380458

381459
char *sql = DeparseTreeNode((Node *) stmt);
382460

@@ -395,6 +473,9 @@ PreprocessAlterDatabaseSetStmt(Node *node, const char *queryString,
395473
* In this stage, we perform validations that we want to ensure before delegating to
396474
* previous utility hooks because it might not be convenient to throw an error in an
397475
* implicit transaction that creates a database.
476+
*
477+
* We also serialize database commands globally by acquiring a Citus specific advisory
478+
* lock based on OCLASS_DATABASE on the first primary worker node.
398479
*/
399480
List *
400481
PreprocessCreateDatabaseStmt(Node *node, const char *queryString,
@@ -405,11 +486,13 @@ PreprocessCreateDatabaseStmt(Node *node, const char *queryString,
405486
return NIL;
406487
}
407488

408-
EnsureCoordinator();
489+
EnsurePropagationToCoordinator();
409490

410491
CreatedbStmt *stmt = castNode(CreatedbStmt, node);
411492
EnsureSupportedCreateDatabaseCommand(stmt);
412493

494+
SerializeDistributedDDLsOnObjectClass(OCLASS_DATABASE, InvalidOid);
495+
413496
return NIL;
414497
}
415498

@@ -430,7 +513,7 @@ PostprocessCreateDatabaseStmt(Node *node, const char *queryString)
430513
return NIL;
431514
}
432515

433-
EnsureCoordinator();
516+
EnsurePropagationToCoordinator();
434517

435518
/*
436519
* Given that CREATE DATABASE doesn't support "IF NOT EXISTS" and we're
@@ -448,7 +531,7 @@ PostprocessCreateDatabaseStmt(Node *node, const char *queryString)
448531
(void *) createDatabaseCommand,
449532
ENABLE_DDL_PROPAGATION);
450533

451-
return NontransactionalNodeDDLTaskList(NON_COORDINATOR_NODES, commands);
534+
return NontransactionalNodeDDLTaskList(REMOTE_NODES, commands);
452535
}
453536

454537

@@ -458,6 +541,9 @@ PostprocessCreateDatabaseStmt(Node *node, const char *queryString)
458541
* all workers to drop the database. Since the DROP DATABASE statement gives error in
459542
* transaction context, we need to use NontransactionalNodeDDLTaskList to send the
460543
* DROP DATABASE statement to the workers.
544+
*
545+
* We also serialize database commands globally by acquiring a Citus specific advisory
546+
* lock based on OCLASS_DATABASE on the first primary worker node.
461547
*/
462548
List *
463549
PreprocessDropDatabaseStmt(Node *node, const char *queryString,
@@ -468,7 +554,7 @@ PreprocessDropDatabaseStmt(Node *node, const char *queryString,
468554
return NIL;
469555
}
470556

471-
EnsureCoordinator();
557+
EnsurePropagationToCoordinator();
472558

473559
DropdbStmt *stmt = (DropdbStmt *) node;
474560

@@ -488,13 +574,15 @@ PreprocessDropDatabaseStmt(Node *node, const char *queryString,
488574
return NIL;
489575
}
490576

577+
SerializeDistributedDDLsOnObjectClass(OCLASS_DATABASE, address->objectId);
578+
491579
char *dropDatabaseCommand = DeparseTreeNode(node);
492580

493581
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
494582
(void *) dropDatabaseCommand,
495583
ENABLE_DDL_PROPAGATION);
496584

497-
return NontransactionalNodeDDLTaskList(NON_COORDINATOR_NODES, commands);
585+
return NontransactionalNodeDDLTaskList(REMOTE_NODES, commands);
498586
}
499587

500588

src/backend/distributed/commands/distribute_object_ops.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ static DistributeObjectOps Database_Set = {
536536
static DistributeObjectOps Database_Rename = {
537537
.deparse = DeparseAlterDatabaseRenameStmt,
538538
.qualify = NULL,
539-
.preprocess = NULL,
539+
.preprocess = PreprocessAlterDatabaseRenameStmt,
540540
.postprocess = PostprocessAlterDatabaseRenameStmt,
541541
.objectType = OBJECT_DATABASE,
542542
.operationType = DIST_OPS_ALTER,

src/backend/distributed/commands/utility_hook.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -726,9 +726,9 @@ citus_ProcessUtilityInternal(PlannedStmt *pstmt,
726726
ereport(NOTICE, (errmsg("Citus partially supports CREATE DATABASE for "
727727
"distributed databases"),
728728
errdetail("Citus does not propagate CREATE DATABASE "
729-
"command to workers"),
729+
"command to other nodes"),
730730
errhint("You can manually create a database and its "
731-
"extensions on workers.")));
731+
"extensions on other nodes.")));
732732
}
733733
}
734734
else if (IsA(parsetree, CreateRoleStmt) && !EnableCreateRolePropagation)

src/backend/distributed/metadata/node_metadata.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2771,12 +2771,24 @@ EnsureCoordinatorIsInMetadata(void)
27712771
{
27722772
bool isCoordinatorInMetadata = false;
27732773
PrimaryNodeForGroup(COORDINATOR_GROUP_ID, &isCoordinatorInMetadata);
2774-
if (!isCoordinatorInMetadata)
2774+
if (isCoordinatorInMetadata)
2775+
{
2776+
return;
2777+
}
2778+
2779+
/* be more descriptive when we're not on coordinator */
2780+
if (IsCoordinator())
27752781
{
27762782
ereport(ERROR, (errmsg("coordinator is not added to the metadata"),
27772783
errhint("Use SELECT citus_set_coordinator_host('<hostname>') "
27782784
"to configure the coordinator hostname")));
27792785
}
2786+
else
2787+
{
2788+
ereport(ERROR, (errmsg("coordinator is not added to the metadata"),
2789+
errhint("Use SELECT citus_set_coordinator_host('<hostname>') "
2790+
"on coordinator to configure the coordinator hostname")));
2791+
}
27802792
}
27812793

27822794

0 commit comments

Comments
 (0)