mirror of https://github.com/postgres/postgres
Previously synchronous replication offered only the ability to confirm that all changes made by a transaction had been transferred to at most one synchronous standby server. This commit extends synchronous replication so that it supports multiple synchronous standby servers. It enables users to consider one or more standby servers as synchronous, and increase the level of transaction durability by ensuring that transaction commits wait for replies from all of those synchronous standbys. Multiple synchronous standby servers are configured in synchronous_standby_names which is extended to support new syntax of 'num_sync ( standby_name [ , ... ] )', where num_sync specifies the number of synchronous standbys that transaction commits need to wait for replies from and standby_name is the name of a standby server. The syntax of 'standby_name [ , ... ]' which was used in 9.5 or before is also still supported. It's the same as new syntax with num_sync=1. This commit doesn't include "quorum commit" feature which was discussed in pgsql-hackers. Synchronous standbys are chosen based on their priorities. synchronous_standby_names determines the priority of each standby for being chosen as a synchronous standby. The standbys whose names appear earlier in the list are given higher priority and will be considered as synchronous. Other standby servers appearing later in this list represent potential synchronous standbys. The regression test for multiple synchronous standbys is not included in this commit. It should come later. Authors: Sawada Masahiko, Beena Emerson, Michael Paquier, Fujii Masao Reviewed-By: Kyotaro Horiguchi, Amit Kapila, Robert Haas, Simon Riggs, Amit Langote, Thomas Munro, Sameer Thakur, Suraj Kharage, Abhijit Menon-Sen, Rajeev Rastogi Many thanks to the various individuals who were involved in discussing and developing this feature.pull/11/head
parent
2143f5e127
commit
989be0810d
@ -1,2 +1,4 @@ |
||||
/repl_gram.c |
||||
/repl_scanner.c |
||||
/syncrep_gram.c |
||||
/syncrep_scanner.c |
||||
|
@ -0,0 +1,86 @@ |
||||
%{ |
||||
/*------------------------------------------------------------------------- |
||||
* |
||||
* syncrep_gram.y - Parser for synchronous_standby_names |
||||
* |
||||
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* src/backend/replication/syncrep_gram.y |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
#include "postgres.h" |
||||
|
||||
#include "replication/syncrep.h" |
||||
#include "utils/formatting.h" |
||||
|
||||
/* Result of the parsing is returned here */ |
||||
SyncRepConfigData *syncrep_parse_result; |
||||
|
||||
static SyncRepConfigData *create_syncrep_config(char *num_sync, List *members); |
||||
|
||||
/* |
||||
* Bison doesn't allocate anything that needs to live across parser calls, |
||||
* so we can easily have it use palloc instead of malloc. This prevents |
||||
* memory leaks if we error out during parsing. Note this only works with |
||||
* bison >= 2.0. However, in bison 1.875 the default is to use alloca() |
||||
* if possible, so there's not really much problem anyhow, at least if |
||||
* you're building with gcc. |
||||
*/ |
||||
#define YYMALLOC palloc |
||||
#define YYFREE pfree |
||||
|
||||
%} |
||||
|
||||
%expect 0 |
||||
%name-prefix="syncrep_yy" |
||||
|
||||
%union |
||||
{ |
||||
char *str; |
||||
List *list; |
||||
SyncRepConfigData *config; |
||||
} |
||||
|
||||
%token <str> NAME NUM |
||||
|
||||
%type <config> result standby_config |
||||
%type <list> standby_list |
||||
%type <str> standby_name |
||||
|
||||
%start result |
||||
|
||||
%% |
||||
result: |
||||
standby_config { syncrep_parse_result = $1; } |
||||
; |
||||
standby_config: |
||||
standby_list { $$ = create_syncrep_config("1", $1); } |
||||
| NUM '(' standby_list ')' { $$ = create_syncrep_config($1, $3); } |
||||
; |
||||
standby_list: |
||||
standby_name { $$ = list_make1($1);} |
||||
| standby_list ',' standby_name { $$ = lappend($1, $3);} |
||||
; |
||||
standby_name: |
||||
NAME { $$ = $1; } |
||||
| NUM { $$ = $1; } |
||||
; |
||||
%% |
||||
|
||||
static SyncRepConfigData * |
||||
create_syncrep_config(char *num_sync, List *members) |
||||
{ |
||||
SyncRepConfigData *config = |
||||
(SyncRepConfigData *) palloc(sizeof(SyncRepConfigData)); |
||||
|
||||
config->num_sync = atoi(num_sync); |
||||
config->members = members; |
||||
return config; |
||||
} |
||||
|
||||
#include "syncrep_scanner.c" |
@ -0,0 +1,144 @@ |
||||
%{ |
||||
/*------------------------------------------------------------------------- |
||||
* |
||||
* syncrep_scanner.l |
||||
* a lexical scanner for synchronous_standby_names |
||||
* |
||||
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* src/backend/replication/syncrep_scanner.l |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "miscadmin.h" |
||||
#include "lib/stringinfo.h" |
||||
|
||||
/* |
||||
* flex emits a yy_fatal_error() function that it calls in response to |
||||
* critical errors like malloc failure, file I/O errors, and detection of |
||||
* internal inconsistency. That function prints a message and calls exit(). |
||||
* Mutate it to instead call ereport(FATAL), which terminates this process. |
||||
* |
||||
* The process that causes this fatal error should be terminated. |
||||
* Otherwise it has to abandon the new setting value of |
||||
* synchronous_standby_names and keep running with the previous one |
||||
* while the other processes switch to the new one. |
||||
* This inconsistency of the setting that each process is based on |
||||
* can cause a serious problem. Though it's basically not good idea to |
||||
* use FATAL here because it can take down the postmaster, |
||||
* we should do that in order to avoid such an inconsistency. |
||||
*/ |
||||
#undef fprintf |
||||
#define fprintf(file, fmt, msg) syncrep_flex_fatal(fmt, msg) |
||||
|
||||
static void |
||||
syncrep_flex_fatal(const char *fmt, const char *msg) |
||||
{ |
||||
ereport(FATAL, (errmsg_internal("%s", msg))); |
||||
} |
||||
|
||||
/* Handles to the buffer that the lexer uses internally */ |
||||
static YY_BUFFER_STATE scanbufhandle; |
||||
|
||||
static StringInfoData xdbuf; |
||||
|
||||
%} |
||||
|
||||
%option 8bit |
||||
%option never-interactive |
||||
%option nounput |
||||
%option noinput |
||||
%option noyywrap |
||||
%option warn |
||||
%option prefix="syncrep_yy" |
||||
|
||||
/* |
||||
* <xd> delimited identifiers (double-quoted identifiers) |
||||
*/ |
||||
%x xd |
||||
|
||||
space [ \t\n\r\f\v] |
||||
|
||||
undquoted_start [^ ,\(\)\"] |
||||
undquoted_cont [^ ,\(\)] |
||||
undquoted_name {undquoted_start}{undquoted_cont}* |
||||
dquoted_name [^\"]+ |
||||
|
||||
/* Double-quoted string */ |
||||
dquote \" |
||||
xdstart {dquote} |
||||
xddouble {dquote}{dquote} |
||||
xdstop {dquote} |
||||
xdinside {dquoted_name} |
||||
|
||||
%% |
||||
{space}+ { /* ignore */ } |
||||
{xdstart} { |
||||
initStringInfo(&xdbuf); |
||||
BEGIN(xd); |
||||
} |
||||
<xd>{xddouble} { |
||||
appendStringInfoChar(&xdbuf, '\"'); |
||||
} |
||||
<xd>{xdinside} { |
||||
appendStringInfoString(&xdbuf, yytext); |
||||
} |
||||
<xd>{xdstop} { |
||||
yylval.str = pstrdup(xdbuf.data); |
||||
pfree(xdbuf.data); |
||||
BEGIN(INITIAL); |
||||
return NAME; |
||||
} |
||||
"," { return ','; } |
||||
"(" { return '('; } |
||||
")" { return ')'; } |
||||
[1-9][0-9]* { |
||||
yylval.str = pstrdup(yytext); |
||||
return NUM; |
||||
} |
||||
{undquoted_name} { |
||||
yylval.str = pstrdup(yytext); |
||||
return NAME; |
||||
} |
||||
%% |
||||
|
||||
void |
||||
yyerror(const char *message) |
||||
{ |
||||
ereport(IsUnderPostmaster ? DEBUG2 : LOG, |
||||
(errcode(ERRCODE_SYNTAX_ERROR), |
||||
errmsg("%s at or near \"%s\"", message, yytext))); |
||||
} |
||||
|
||||
void |
||||
syncrep_scanner_init(const char *str) |
||||
{ |
||||
Size slen = strlen(str); |
||||
char *scanbuf; |
||||
|
||||
/* |
||||
* Might be left over after ereport() |
||||
*/ |
||||
if (YY_CURRENT_BUFFER) |
||||
yy_delete_buffer(YY_CURRENT_BUFFER); |
||||
|
||||
/* |
||||
* Make a scan buffer with special termination needed by flex. |
||||
*/ |
||||
scanbuf = (char *) palloc(slen + 2); |
||||
memcpy(scanbuf, str, slen); |
||||
scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR; |
||||
scanbufhandle = yy_scan_buffer(scanbuf, slen + 2); |
||||
} |
||||
|
||||
void |
||||
syncrep_scanner_finish(void) |
||||
{ |
||||
yy_delete_buffer(scanbufhandle); |
||||
scanbufhandle = NULL; |
||||
} |
Loading…
Reference in new issue