41
41
#include "distributed/metadata_utility.h"
42
42
#include "distributed/multi_executor.h"
43
43
#include "distributed/relation_access_tracking.h"
44
+ #include "distributed/serialize_distributed_ddls.h"
44
45
#include "distributed/worker_protocol.h"
45
46
#include "distributed/worker_transaction.h"
46
47
@@ -161,6 +162,10 @@ get_database_owner(Oid dbId)
161
162
*
162
163
* In this stage we can prepare the commands that need to be run on all workers to grant
163
164
* 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.
164
169
*/
165
170
List *
166
171
PreprocessGrantOnDatabaseStmt (Node * node , const char * queryString ,
@@ -183,6 +188,24 @@ PreprocessGrantOnDatabaseStmt(Node *node, const char *queryString,
183
188
184
189
EnsureCoordinator ();
185
190
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
+
186
209
List * originalObjects = stmt -> objects ;
187
210
188
211
stmt -> objects = distributedDatabases ;
@@ -248,6 +271,9 @@ IsSetTablespaceStatement(AlterDatabaseStmt *stmt)
248
271
*
249
272
* In this stage we can prepare the commands that need to be run on all workers to grant
250
273
* 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.
251
277
*/
252
278
List *
253
279
PreprocessAlterDatabaseStmt (Node * node , const char * queryString ,
@@ -264,6 +290,7 @@ PreprocessAlterDatabaseStmt(Node *node, const char *queryString,
264
290
}
265
291
266
292
EnsureCoordinator ();
293
+ SerializeDistributedDDLsOnObjectClass (OCLASS_DATABASE , dbAddress -> objectId );
267
294
268
295
char * sql = DeparseTreeNode ((Node * ) stmt );
269
296
@@ -296,6 +323,9 @@ PreprocessAlterDatabaseStmt(Node *node, const char *queryString,
296
323
*
297
324
* In this stage we can prepare the commands that need to be run on all workers to grant
298
325
* 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.
299
329
*/
300
330
List *
301
331
PreprocessAlterDatabaseRefreshCollStmt (Node * node , const char * queryString ,
@@ -312,6 +342,7 @@ PreprocessAlterDatabaseRefreshCollStmt(Node *node, const char *queryString,
312
342
}
313
343
314
344
EnsureCoordinator ();
345
+ SerializeDistributedDDLsOnObjectClass (OCLASS_DATABASE , dbAddress -> objectId );
315
346
316
347
char * sql = DeparseTreeNode ((Node * ) stmt );
317
348
@@ -325,8 +356,51 @@ PreprocessAlterDatabaseRefreshCollStmt(Node *node, const char *queryString,
325
356
326
357
#endif
327
358
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
+
328
402
/*
329
- * PreprocessAlterDatabaseRenameStmt is executed before the statement is applied to the local
403
+ * PostprocessAlterDatabaseRenameStmt is executed after the statement is applied to the local
330
404
* postgres instance. In this stage we prepare ALTER DATABASE RENAME statement to be run on
331
405
* all workers.
332
406
*/
@@ -361,6 +435,9 @@ PostprocessAlterDatabaseRenameStmt(Node *node, const char *queryString)
361
435
*
362
436
* In this stage we can prepare the commands that need to be run on all workers to grant
363
437
* 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.
364
441
*/
365
442
List *
366
443
PreprocessAlterDatabaseSetStmt (Node * node , const char * queryString ,
@@ -377,6 +454,7 @@ PreprocessAlterDatabaseSetStmt(Node *node, const char *queryString,
377
454
}
378
455
379
456
EnsureCoordinator ();
457
+ SerializeDistributedDDLsOnObjectClass (OCLASS_DATABASE , dbAddress -> objectId );
380
458
381
459
char * sql = DeparseTreeNode ((Node * ) stmt );
382
460
@@ -395,6 +473,9 @@ PreprocessAlterDatabaseSetStmt(Node *node, const char *queryString,
395
473
* In this stage, we perform validations that we want to ensure before delegating to
396
474
* previous utility hooks because it might not be convenient to throw an error in an
397
475
* 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.
398
479
*/
399
480
List *
400
481
PreprocessCreateDatabaseStmt (Node * node , const char * queryString ,
@@ -405,11 +486,13 @@ PreprocessCreateDatabaseStmt(Node *node, const char *queryString,
405
486
return NIL ;
406
487
}
407
488
408
- EnsureCoordinator ();
489
+ EnsurePropagationToCoordinator ();
409
490
410
491
CreatedbStmt * stmt = castNode (CreatedbStmt , node );
411
492
EnsureSupportedCreateDatabaseCommand (stmt );
412
493
494
+ SerializeDistributedDDLsOnObjectClass (OCLASS_DATABASE , InvalidOid );
495
+
413
496
return NIL ;
414
497
}
415
498
@@ -430,7 +513,7 @@ PostprocessCreateDatabaseStmt(Node *node, const char *queryString)
430
513
return NIL ;
431
514
}
432
515
433
- EnsureCoordinator ();
516
+ EnsurePropagationToCoordinator ();
434
517
435
518
/*
436
519
* Given that CREATE DATABASE doesn't support "IF NOT EXISTS" and we're
@@ -448,7 +531,7 @@ PostprocessCreateDatabaseStmt(Node *node, const char *queryString)
448
531
(void * ) createDatabaseCommand ,
449
532
ENABLE_DDL_PROPAGATION );
450
533
451
- return NontransactionalNodeDDLTaskList (NON_COORDINATOR_NODES , commands );
534
+ return NontransactionalNodeDDLTaskList (REMOTE_NODES , commands );
452
535
}
453
536
454
537
@@ -458,6 +541,9 @@ PostprocessCreateDatabaseStmt(Node *node, const char *queryString)
458
541
* all workers to drop the database. Since the DROP DATABASE statement gives error in
459
542
* transaction context, we need to use NontransactionalNodeDDLTaskList to send the
460
543
* 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.
461
547
*/
462
548
List *
463
549
PreprocessDropDatabaseStmt (Node * node , const char * queryString ,
@@ -468,7 +554,7 @@ PreprocessDropDatabaseStmt(Node *node, const char *queryString,
468
554
return NIL ;
469
555
}
470
556
471
- EnsureCoordinator ();
557
+ EnsurePropagationToCoordinator ();
472
558
473
559
DropdbStmt * stmt = (DropdbStmt * ) node ;
474
560
@@ -488,13 +574,15 @@ PreprocessDropDatabaseStmt(Node *node, const char *queryString,
488
574
return NIL ;
489
575
}
490
576
577
+ SerializeDistributedDDLsOnObjectClass (OCLASS_DATABASE , address -> objectId );
578
+
491
579
char * dropDatabaseCommand = DeparseTreeNode (node );
492
580
493
581
List * commands = list_make3 (DISABLE_DDL_PROPAGATION ,
494
582
(void * ) dropDatabaseCommand ,
495
583
ENABLE_DDL_PROPAGATION );
496
584
497
- return NontransactionalNodeDDLTaskList (NON_COORDINATOR_NODES , commands );
585
+ return NontransactionalNodeDDLTaskList (REMOTE_NODES , commands );
498
586
}
499
587
500
588
0 commit comments