2
2
#include "miscadmin.h" /* PostmasterPid */
3
3
#include "multimaster.h"
4
4
#include "state.h"
5
+ #include "executor/spi.h"
6
+ #include "utils/snapmgr.h"
7
+ #include "nodes/makefuncs.h"
8
+ #include "catalog/namespace.h"
5
9
6
10
char const * const MtmNeighborEventMnem [] =
7
11
{
@@ -27,6 +31,9 @@ char const* const MtmEventMnem[] =
27
31
28
32
static int MtmRefereeGetWinner (void );
29
33
static bool MtmRefereeClearWinner (void );
34
+ static int MtmRefereeReadSaved (void );
35
+
36
+ static bool mtm_state_initialized ;
30
37
31
38
// XXXX: allocate in context and clean it
32
39
static char *
@@ -434,6 +441,13 @@ MtmRefreshClusterStatus()
434
441
{
435
442
int winner_node_id = MtmRefereeGetWinner ();
436
443
444
+ /* We also can have old value. Do that only from single mtm-monitor process */
445
+ if (winner_node_id <= 0 && !mtm_state_initialized )
446
+ {
447
+ winner_node_id = MtmRefereeReadSaved ();
448
+ mtm_state_initialized = true;
449
+ }
450
+
437
451
if (winner_node_id > 0 )
438
452
{
439
453
Mtm -> refereeWinnerId = winner_node_id ;
@@ -555,13 +569,70 @@ MtmRefreshClusterStatus()
555
569
MtmUnlock ();
556
570
}
557
571
572
+ /*
573
+ * Referee caches decision in mtm.referee_decision
574
+ */
575
+ static bool
576
+ MtmRefereeHasLocalTable ()
577
+ {
578
+ RangeVar * rv ;
579
+ Oid rel_oid ;
580
+
581
+ StartTransactionCommand ();
582
+ rv = makeRangeVar (MULTIMASTER_SCHEMA_NAME , "referee_decision" , -1 );
583
+ rel_oid = RangeVarGetRelid (rv , NoLock , true);
584
+ CommitTransactionCommand ();
585
+
586
+ return OidIsValid (rel_oid );
587
+ }
588
+
589
+ static int
590
+ MtmRefereeReadSaved (void )
591
+ {
592
+ int winner = -1 ;
593
+ int rc ;
594
+
595
+ if (!MtmRefereeHasLocalTable ())
596
+ return -1 ;
597
+
598
+ /* Save result locally */
599
+ StartTransactionCommand ();
600
+ SPI_connect ();
601
+ PushActiveSnapshot (GetTransactionSnapshot ());
602
+ rc = SPI_execute ("select node_id from mtm.referee_decision where key = 'winner';" , true, 0 );
603
+ if (rc != SPI_OK_SELECT )
604
+ {
605
+ MTM_ELOG (WARNING , "Failed to load referee decision" );
606
+ }
607
+ else if (SPI_processed > 0 )
608
+ {
609
+ bool isnull ;
610
+ winner = DatumGetInt32 (SPI_getbinval (SPI_tuptable -> vals [0 ], SPI_tuptable -> tupdesc , 1 , & isnull ));
611
+ Assert (SPI_processed == 1 );
612
+ Assert (!isnull );
613
+ }
614
+ else
615
+ {
616
+ /* no saved decision found */
617
+ Assert (SPI_processed == 0 );
618
+ }
619
+ SPI_finish ();
620
+ PopActiveSnapshot ();
621
+ CommitTransactionCommand ();
622
+
623
+ MTM_LOG1 ("Read saved referee decision, winner=%d." , winner );
624
+ return winner ;
625
+ }
626
+
558
627
static int
559
628
MtmRefereeGetWinner (void )
560
629
{
561
630
PGconn * conn ;
562
631
PGresult * res ;
563
632
char sql [128 ];
564
633
int winner_node_id ;
634
+ int old_winner = -1 ;
635
+ int rc ;
565
636
566
637
conn = PQconnectdb_safe (MtmRefereeConnStr , 5 );
567
638
if (PQstatus (conn ) != CONNECTION_OK )
@@ -599,6 +670,48 @@ MtmRefereeGetWinner(void)
599
670
/* Ok, we finally got it! */
600
671
PQclear (res );
601
672
PQfinish (conn );
673
+
674
+ /* Save result locally */
675
+ if (MtmRefereeHasLocalTable ())
676
+ {
677
+ MtmEnforceLocalTx = true;
678
+ StartTransactionCommand ();
679
+ SPI_connect ();
680
+ PushActiveSnapshot (GetTransactionSnapshot ());
681
+ /* Check old value if any */
682
+ rc = SPI_execute ("select node_id from mtm.referee_decision where key = 'winner';" , true, 0 );
683
+ if (rc != SPI_OK_SELECT )
684
+ {
685
+ MTM_ELOG (WARNING , "Failed to load previous referee decision" );
686
+ }
687
+ else if (SPI_processed > 0 )
688
+ {
689
+ bool isnull ;
690
+ old_winner = DatumGetInt32 (SPI_getbinval (SPI_tuptable -> vals [0 ], SPI_tuptable -> tupdesc , 1 , & isnull ));
691
+ Assert (SPI_processed == 1 );
692
+ Assert (!isnull );
693
+ }
694
+ else
695
+ {
696
+ /* no saved decision found */
697
+ Assert (SPI_processed == 0 );
698
+ }
699
+ /* Update actual key */
700
+ sprintf (sql ,
701
+ "insert into mtm.referee_decision values ('winner', %d) on conflict(key) do nothing;" ,
702
+ winner_node_id );
703
+ rc = SPI_execute (sql , false, 0 );
704
+ SPI_finish ();
705
+ if (rc < 0 )
706
+ MTM_ELOG (WARNING , "Failed to save referee decision, but proceeding anyway" );
707
+ PopActiveSnapshot ();
708
+ CommitTransactionCommand ();
709
+ MtmEnforceLocalTx = false;
710
+
711
+ if (old_winner > 0 && old_winner != winner_node_id )
712
+ MTM_LOG1 ("WARNING Overriding old referee decision (%d) with new one (%d)" , old_winner , winner_node_id );
713
+ }
714
+
602
715
MTM_LOG1 ("Got referee response, winner node_id=%d." , winner_node_id );
603
716
return winner_node_id ;
604
717
}
@@ -609,6 +722,32 @@ MtmRefereeClearWinner(void)
609
722
PGconn * conn ;
610
723
PGresult * res ;
611
724
char * response ;
725
+ int rc ;
726
+
727
+ /*
728
+ * Delete result locally first.
729
+ *
730
+ * If we delete decision from referee but fail to delete local cached
731
+ * that will be pretty bad -- on the next reboot we can read
732
+ * stale referee decision and on next failure end up with two masters.
733
+ * So just delete local cache first.
734
+ */
735
+ if (MtmRefereeHasLocalTable ())
736
+ {
737
+ StartTransactionCommand ();
738
+ SPI_connect ();
739
+ PushActiveSnapshot (GetTransactionSnapshot ());
740
+ rc = SPI_execute ("delete from mtm.referee_decision where key = 'winner'" , false, 0 );
741
+ SPI_finish ();
742
+ PopActiveSnapshot ();
743
+ CommitTransactionCommand ();
744
+ if (rc < 0 )
745
+ {
746
+ MTM_ELOG (WARNING , "Failed to save referee decision, proceeding anyway" );
747
+ return false;
748
+ }
749
+ }
750
+
612
751
613
752
conn = PQconnectdb_safe (MtmRefereeConnStr , 5 );
614
753
if (PQstatus (conn ) != CONNECTION_OK )
0 commit comments