Skip to content

Conversation

ghost
Copy link

@ghost ghost commented Feb 8, 2018

@sgotti please review the pull request. If it is ok then let me create auto tests. This PR is the continuance of PR #410 started from the scratch

Copy link
Member

@sgotti sgotti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@saranhada Thanks for the PR. First review pass.

@@ -397,6 +399,23 @@ func (os *ClusterSpec) Validate() error {
if s.InitMode == nil {
return fmt.Errorf("initMode undefined")
}
if len(s.AdditionalReplicationSlotNames) > 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to check the length > 0. The range will just take care of it.

symbolsNameFilter := regexp.MustCompile("[_0-9]")

for _, replSlotName := range s.AdditionalReplicationSlotNames {
if trimmedSlotName := strings.TrimSpace(replSlotName); len(trimmedSlotName) == 0 ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can simplify it:

@@ -397,6 +399,23 @@ func (os *ClusterSpec) Validate() error {
if s.InitMode == nil {
return fmt.Errorf("initMode undefined")
}
if len(s.AdditionalReplicationSlotNames) > 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please put this in its own function so we can add unit tests.

@@ -529,7 +548,8 @@ type DBSpec struct {
UsePgrewind bool `json:"usePgrewind,omitempty"`
// AdditionalWalSenders defines the number of additional wal_senders in
// addition to the ones internally defined by stolon
AdditionalWalSenders uint16 `json:"additionalWalSenders"`
AdditionalWalSenders uint16 `json:"additionalWalSenders"`
AdditionalReplicationSlotNames []string `json:"additionalReplicationSlotNames"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing comment

}

// drop unnecessary slots
if len(slotsToDrop) > 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to check the length > 0. The range will just take care of it.

// create required slots
if len(slotsToCreate) > 0 {
for _, createSlotName := range slotsToCreate {
log.Infow("Create slot", "createSlotName", createSlotName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log.Infow("creating replication slot", "createSlotName", createSlotName)

if !slotShouldSurvive { // slot not detected as additional, so schedule the removal
slotsToDrop = append(slotsToDrop, removeCandidateSlot)
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be simplified in (not tested):

for _, rs := range notStolonSlots {
	if !util.StringInSlice(addonSlotNames, replSlot) {
		slotsToDrop = append(slotsToDrop, rs)
	}
}

if !slotExists {
slotsToCreate = append(slotsToCreate, strings.TrimSpace(addReplicationSlotName))
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be simplified in (not tested):

for _, rs := range addonSlotNames {
	if !util.StringInSlice(notStolonSlots, replSlot) {
		slotsToCreate = append(slotsToCreate, rs)
	}
}

@@ -400,6 +400,7 @@ func (s *Sentinel) setDBSpecFromClusterSpec(cd *cluster.ClusterData) {
db.Spec.FollowConfig.StandbySettings = clusterSpec.StandbySettings
}
db.Spec.AdditionalWalSenders = *clusterSpec.AdditionalWalSenders
db.Spec.AdditionalReplicationSlotNames = clusterSpec.AdditionalReplicationSlotNames
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't needed and misleading since it's adding this to all the clusterdata dbs.

Since the replication slots are created only on the master instance (primary postgres instance or master postgres standby instance in a Standby stolon cluster) the keeper could just read the clusterspec.

@@ -25,6 +25,7 @@ Some options in a running cluster specification can be changed to update the des
| minSynchronousStandbys | minimum number of required synchronous standbys when synchronous replication is enabled (only set this to a value > 1 when using PostgreSQL >= 9.6) | no | uint16 | 1 |
| maxSynchronousStandbys | maximum number of required synchronous standbys when synchronous replication is enabled (only set this to a value > 1 when using PostgreSQL >= 9.6) | no | uint16 | 1 |
| additionalWalSenders | number of additional wal_senders in addition to the ones internally defined by stolon, useful to provide enough wal senders for external standbys (changing this value requires an instance restart) | no | uint16 | 5 |
| additionalReplicationSlotNames | a list of additional replication slots which are always created by the master postgres instance | no | []string | null |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a list of additional replication slots to be created on the master postgres instance. Replication slots not defined here will be dropped.

@@ -842,6 +835,53 @@ func (p *PostgresKeeper) updateReplSlots(dbLocalState *DBLocalState, followersUI
return nil
}

func (p *PostgresKeeper) recreateAddonReplicationSlots(existingReplicationSlots []string, addonSlotNames []string) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/recreateAddonReplicationSlots/updateAdditionalReplSlots/

log.Infow("Dropping replication slot", "dropSlotName", dropSlotName)
if err := p.pgm.DropReplicationSlot(dropSlotName); err != nil {
log.Errorw("Failed to drop replication slot", "slotName", dropSlotName, zap.Error(err))
return err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ignore dropping errors like done in updateReplSlot since they can be in use and this will fail.


// create required slots
for _, createSlotName := range slotsToCreate {
log.Infow("Creatint replication slot", "createSlotName", createSlotName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log.Infow("creating replication slot", "slotName", createSlotName)


// drop unnecessary slots
for _, dropSlotName := range slotsToDrop {
log.Infow("Dropping replication slot", "dropSlotName", dropSlotName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use capital letters in logs.

Copy link
Member

@sgotti sgotti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were some unhandled comments from previous review, readded them.

for _, createSlotName := range slotsToCreate {
log.Infow("Creatint replication slot", "createSlotName", createSlotName)
if err := p.pgm.CreateReplicationSlot(createSlotName); err != nil {
log.Errorw("Failed to create replication slot", "slotName", createSlotName, zap.Error(err))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use capital letters in logs.


// drop unnecessary slots
for _, dropSlotName := range slotsToDrop {
log.Infow("Dropping replication slot", "dropSlotName", dropSlotName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log.Infow("dropping replication slot", "slotName", dropSlotName)

return
}

if err = p.updateReplSlots(currentReplicationSlots, dbls, followersUIDs); err != nil {
log.Errorw("error updating replication slots", zap.Error(err))
return
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should call updateAdditionalReplSlots also here

@@ -224,7 +225,8 @@ type ClusterSpec struct {
MaxSynchronousStandbys *uint16 `json:"maxSynchronousStandbys,omitempty"`
// AdditionalWalSenders defines the number of additional wal_senders in
// addition to the ones internally defined by stolon
AdditionalWalSenders *uint16 `json:"additionalWalSenders"`
AdditionalWalSenders *uint16 `json:"additionalWalSenders"`
AdditionalReplicationSlotNames []string `json:"additionalReplicationSlotNames"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing comment

return nil
}

rp := regexp.MustCompile("[_0-9a-z]+")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


for _, replSlotName := range s.AdditionalReplicationSlotNames {
if trimmedSlotName := strings.TrimSpace(replSlotName); len(trimmedSlotName) == 0 ||
len(rp.ReplaceAllString(trimmedSlotName, "")) > 0 ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't utils.IsValidReplSlotName(replSlotName) enough? I don't see the need to trim space, \n etc... it should already be catched by it. If it's not enough then utils.IsValidReplSlotName should be fixed since the required check belong there.

I don't see the check for IsStolonName to error on repl slots starting with _stolon see my previous review comment: this function should just call two functions: #434 (comment)

An unit test is needed to prove that all works correctly.

@sgotti
Copy link
Member

sgotti commented Feb 14, 2018

@saranhada to be speed up things, since I noticed some of my comments weren't addresses as I expected I changed your commit to fix it:

  • validateReplicationSlot is now a generic function that takes one replslot and only uses util.IsValidReplSlotName and IsStolonName, not trimming or other uneeded things: Additional replication slots #434 (comment)

  • added a test for validateReplicationSlot to check that it works correcly

  • fixed the dbSpec option since it wasn't needed. Now it's a generic DBSpec option where we ask the keeper which additional replslots to use. Please note (written in the TODOs) that now they are managed only on the master instance.

  • fixed missing logging after call to refreshReplicationSlots

  • simplified updateAdditionalReplSlots.

  • On postgres >= 10 there's the concept of temporary replication slot. We should ignore them.

You can see and take the commit here: 00007b2

@sgotti
Copy link
Member

sgotti commented Feb 15, 2018

@saranhada I also added integration test to verify that all is working as expected. Please take a look and take the commit here: sgotti@c3ab6ea

* Add a cluster spec option `additionalMasterReplicationSlots` to let the user
define additional custom replication slots on the stolon master instance. All
the replication slots not defined here (i.e manually created) will be removed.
So user should rely on this option when they need to create new replication
slots.

* Ignore temporary replication slots (PostgreSQL >= 10).

* Add unit tests and integration tests.
@sgotti
Copy link
Member

sgotti commented Feb 23, 2018

@saranhada Thanks! Merging.

@sgotti sgotti merged commit 15164a3 into sorintlab:master Feb 23, 2018
sgotti added a commit that referenced this pull request Feb 23, 2018
@sgotti sgotti added this to the 0.10.0 milestone Mar 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant