-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit bf79600
Showing
10 changed files
with
2,070 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# -*- yaml -*- | ||
# git ls-files -i -x '*.[ch]' | xargs clang-format -i | ||
--- | ||
Language: Cpp | ||
# BasedOnStyle: LLVM | ||
|
||
# true would be better here. but it's bugged in combination with | ||
# "PointerAlignment: Right" which we also use as is more important | ||
AlignConsecutiveDeclarations: false | ||
AlignEscapedNewlines: Right | ||
AllowShortFunctionsOnASingleLine: None | ||
AlwaysBreakAfterDefinitionReturnType: true | ||
BreakBeforeBraces: Allman | ||
BreakBeforeTernaryOperators: false | ||
BreakConstructorInitializersBeforeComma: true | ||
BreakStringLiterals: false | ||
ColumnLimit: 79 | ||
ForEachMacros: | ||
- foreach | ||
- forboth | ||
- dlist_foreach | ||
- dlist_foreach_modify | ||
- slist_foreach | ||
- slist_foreach_modify | ||
IncludeBlocks: Preserve | ||
IncludeCategories: # c.h and postgres.h should be first | ||
- Regex: '.*' | ||
Priority: 1 | ||
- Regex: '^<c\.h>' | ||
Priority: -1 | ||
- Regex: '^<postgres\.h>' | ||
Priority: -1 | ||
IndentCaseLabels: true | ||
IndentWidth: 4 | ||
MacroBlockBegin: "PG_TRY();|PG_CATCH();" | ||
MacroBlockEnd: "PG_END_TRY();" | ||
MaxEmptyLinesToKeep: 3 | ||
PointerAlignment: Right | ||
SpaceAfterCStyleCast: true | ||
TabWidth: 4 | ||
UseTab: Always | ||
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
root = true | ||
|
||
[*.{c,h,pl,pm}] | ||
indent_style = tab | ||
indent_size = tab | ||
tab_width = 4 | ||
|
||
[*.{sql,md,yml}] | ||
indent_style = space | ||
indent_size = 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
tmp_check*/ | ||
*~ | ||
*.swo | ||
*.swp | ||
*.o | ||
*.so | ||
*.gcov | ||
*.gcov.out | ||
*.gcda | ||
*.gcno | ||
*.bc | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
Postgres Failover Slots (pg_failover_slots) | ||
|
||
Copyright (c) 2023, EnterpriseDB Corporation. | ||
|
||
Permission to use, copy, modify, and distribute this software and its | ||
documentation for any purpose, without fee, and without a written agreement is | ||
hereby granted, provided that the above copyright notice and this paragraph and | ||
the following two paragraphs appear in all copies. | ||
|
||
IN NO EVENT SHALL ENTERPRISEDB CORPORATION BE LIABLE TO ANY PARTY FOR | ||
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST | ||
PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF | ||
ENTERPRISEDB CORPORATION HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|
||
ENTERPRISEDB CORPORATION SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | ||
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND | ||
ENTERPRISEDB CORPORATION HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, | ||
UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
MODULE_big = pg_failover_slots | ||
OBJS = pg_failover_slots.o | ||
|
||
PG_CPPFLAGS += -I $(libpq_srcdir) | ||
SHLIB_LINK += $(libpq) | ||
|
||
TAP_TESTS = 1 | ||
|
||
PG_CONFIG = pg_config | ||
PGXS := $(shell $(PG_CONFIG) --pgxs) | ||
include $(PGXS) | ||
|
||
prove_installcheck: $(pgxsdir)/src/test/perl/$(core_perl_module) install | ||
rm -rf $(CURDIR)/tmp_check | ||
mkdir -p $(CURDIR)/tmp_check &&\ | ||
PERL5LIB="$${PERL5LIB}:$(srcdir)/t:$(pgxsdir)/src/test/perl" \ | ||
PG_VERSION_NUM='$(VERSION_NUM)' \ | ||
TESTDIR='$(CURDIR)' \ | ||
SRCDIR='$(srcdir)' \ | ||
PATH="$(TEST_PATH_PREFIX):$(PATH)" \ | ||
PGPORT='6$(DEF_PGPORT)' \ | ||
top_builddir='$(CURDIR)/$(top_builddir)' \ | ||
PG_REGRESS='$(pgxsdir)/src/test/regress/pg_regress' \ | ||
$(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) \ | ||
$(addprefix $(srcdir)/,$(or $(PROVE_TESTS),t/*.pl)) | ||
|
||
check_prove: prove_installcheck |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
# pg_failover_slots | ||
|
||
An extension that makes logical replication slots practically usable across | ||
physical failover. | ||
|
||
This extension does the following: | ||
|
||
- copy any missing slots from primary to standby | ||
- remove any slots from standby that are not found on primary | ||
- periodically synchronize position of slots on standby based on primary | ||
- ensure that selected standbys receive data before any of the logical slot | ||
walsenders can send data to consumers | ||
|
||
PostgreSQL 11 on higher is required. | ||
|
||
## How to check the standby is ready | ||
|
||
The slots are not synchronized to the standby immediately, because of | ||
consistency reasons. The standby can be too behind logical slots, or too ahead | ||
of logical slots on primary when the pg_failover_slots extension is activated, | ||
so the extension does verification and only synchronizes slots when it's | ||
actually safe. | ||
|
||
This, however brings a need to verify that the slots are synchronized and | ||
that the standby is actually ready to be a failover target with consistent | ||
logical decoding for all slots. This only needs to be done initially, once | ||
the slots are synchronized the first time, they will always be consistent as | ||
long as the extension is active in the cluster. | ||
|
||
The check for whether slots are fully synchronized with primary is relatively | ||
simple. The slots just need to be present in `pg_replication_slots` view on | ||
standby and have `active` state `false`. An `active` state `true` means the | ||
slots is currently being initialized. | ||
|
||
For example consider the following psql session: | ||
|
||
```psql | ||
# SELECT slot_name, active FROM pg_replication_slots WHERE slot_type = 'logical'; | ||
slot_name | active | ||
-----------------+-------- | ||
regression_slot1 | f | ||
regression_slot2 | f | ||
regression_slot3 | t | ||
``` | ||
|
||
This means that slots `regression_slot1` and `regression_slot2` are synchronized | ||
from primary to standby and `regression_slot3` is still being synchronized. If | ||
failover happens at this stage, the `regression_slot3` will be lost. | ||
|
||
Now let's wait a little and query again: | ||
|
||
```psql | ||
# SELECT slot_name, active FROM pg_replication_slots WHERE slot_type = 'logical'; | ||
slot_name | active | ||
-----------------+-------- | ||
regression_slot1 | f | ||
regression_slot2 | f | ||
regression_slot3 | f | ||
``` | ||
|
||
Now all the the three slots are synchronized and the standby can be used | ||
for failover without losing logical decoding state for any of them. | ||
|
||
## Configuration options | ||
|
||
The extension itself must be added to `shared_preload_libraries` on both the | ||
primary instance as well as any standby that is used for high availability | ||
(failover or switchover) purposes. | ||
|
||
The behavior of pg_failover_slots is configurable using these configuration | ||
options (set in `postgresql.conf`). | ||
|
||
### pg_failover_slots.synchronize_slot_names | ||
|
||
This standby option allows setting which logical slots should be synchronized | ||
to this physical standby. It's a comma-separated list of slot filters. | ||
|
||
A slot filter is defined as `key:value` pair (separated by colon) where `key` | ||
can be one of: | ||
|
||
- `name` - specifies to match exact slot name | ||
- `name_like` - specifies to match slot name against SQL `LIKE` expression | ||
- `plugin` - specifies to match slot plugin name against the value | ||
|
||
The `key` can be omitted and will default to `name` in that case. | ||
|
||
For example, `'my_slot_name,plugin:test_decoding'` will | ||
synchronize the slot named "my_slot_name" and any slots that use the test_decoding plugin. | ||
|
||
If this is set to an empty string, no slots will be synchronized to this physical | ||
standby. | ||
|
||
The default value is `'name_like:%'`, which means all logical replication slots | ||
will be synchronized. | ||
|
||
|
||
### pg_failover_slots.drop_extra_slots | ||
|
||
This standby option controls what happens to extra slots on the standby that are | ||
not found on the primary using the `pg_failover_slots.synchronize_slot_names` filter. | ||
If it's set to true (which is the default), they will be dropped, otherwise | ||
they will be kept. | ||
|
||
### pg_failover_slots.primary_dsn | ||
|
||
A standby option for specifying the connection string to use to connect to the | ||
primary when fetching slot information. | ||
|
||
If empty (default), then use same connection string as `primary_conninfo`. | ||
|
||
Note that `primary_conninfo` cannot be used if there is a `password` field in | ||
the connection string because it gets obfuscated by PostgreSQL and | ||
pg_failover_slots can't actually see the password. In this case, | ||
`pg_failover_slots.primary_dsn` must be configured. | ||
|
||
### pg_failover_slots.standby_slot_names | ||
|
||
This option is typically used in failover configurations to ensure that the | ||
failover-candidate streaming physical replica(s) have received and flushed | ||
all changes before they ever become visible to any subscribers. That guarantees | ||
that a commit cannot vanish on failover to a standby for the consumer of a logical | ||
slot. | ||
|
||
Replication slots whose names are listed in the comma-separated | ||
`pg_failover_slots.standby_slot_names` list are treated specially by the | ||
walsender on the primary. | ||
|
||
Logical replication walsenders will ensure that all local changes are sent and | ||
flushed to the replication slots in `pg_failover_slots.standby_slot_names` | ||
before the walsender sends those changes for the logical replication slots. | ||
Effectively, it provides a synchronous replication barrier between the named | ||
list of slots and all the consumers of logically decoded streams from walsender. | ||
|
||
Any replication slot may be listed in `pg_failover_slots.standby_slot_names`; | ||
both logical and physical slots work, but it's generally used for physical | ||
slots. | ||
|
||
Without this safeguard, two anomalies are possible where a commit can be | ||
received by a subscriber and then vanish from the provider on failover because | ||
the failover candidate hadn't received it yet: | ||
|
||
* For 1+ subscribers, the subscriber may have applied the change but the new | ||
provider may execute new transactions that conflict with the received change, | ||
as it never happened as far as the provider is concerned; | ||
|
||
and/or | ||
|
||
* For 2+ subscribers, at the time of failover, not all subscribers have applied | ||
the change. The subscribers now have inconsistent and irreconcilable states | ||
because the subscribers that didn't receive the commit have no way to get it | ||
now. | ||
|
||
Setting `pg_failover_slots.standby_slot_names` will (by design) cause subscribers to | ||
lag behind the provider if the provider's failover-candidate replica(s) are not | ||
keeping up. Monitoring is thus essential. | ||
|
||
### pg_failover_slots.standby_slots_min_confirmed | ||
|
||
Controls how many of the `pg_failover_slots.standby_slot_names` have to | ||
confirm before we send data through the logical replication | ||
slots. Setting -1 (the default) means to wait for all entries in | ||
`pg_failover_slots.standby_slot_names`. |
Oops, something went wrong.