Fix replication with context

The normal connection context timeout cancels the current query. That isn't
appropriate for a replication connection.
v3-numeric-wip
Jack Christensen 2017-03-05 14:00:38 -06:00
parent 071f4cc2ad
commit 5702f34407
2 changed files with 43 additions and 10 deletions

View File

@ -270,16 +270,43 @@ func (rc *ReplicationConn) readReplicationMessage() (r *ReplicationMessage, err
//
// This returns the context error when there is no replication message before
// the context is canceled.
func (rc *ReplicationConn) WaitForReplicationMessage(ctx context.Context) (r *ReplicationMessage, err error) {
err = rc.c.initContext(ctx)
if err != nil {
return nil, err
func (rc *ReplicationConn) WaitForReplicationMessage(ctx context.Context) (*ReplicationMessage, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
defer func() {
err = rc.c.termContext(err)
go func() {
select {
case <-ctx.Done():
if err := rc.c.conn.SetDeadline(time.Now()); err != nil {
rc.Close() // Close connection if unable to set deadline
return
}
rc.c.closedChan <- ctx.Err()
case <-rc.c.doneChan:
}
}()
return rc.readReplicationMessage()
r, opErr := rc.readReplicationMessage()
var err error
select {
case err = <-rc.c.closedChan:
if err := rc.c.conn.SetDeadline(time.Time{}); err != nil {
rc.Close() // Close connection if unable to disable deadline
return nil, err
}
if opErr == nil {
err = nil
}
case rc.c.doneChan <- struct{}{}:
err = opErr
}
return r, err
}
func (rc *ReplicationConn) sendReplicationModeQuery(sql string) (*Rows, error) {

View File

@ -3,12 +3,13 @@ package pgx_test
import (
"context"
"fmt"
"github.com/jackc/pgx"
"reflect"
"strconv"
"strings"
"testing"
"time"
"github.com/jackc/pgx"
)
// This function uses a postgresql 9.6 specific column
@ -47,14 +48,19 @@ func TestSimpleReplicationConnection(t *testing.T) {
}
conn := mustConnect(t, *replicationConnConfig)
defer closeConn(t, conn)
defer func() {
// Ensure replication slot is destroyed, but don't check for errors as it
// should have already been destroyed.
conn.Exec("select pg_drop_replication_slot('pgx_test')")
closeConn(t, conn)
}()
replicationConn := mustReplicationConnect(t, *replicationConnConfig)
defer closeReplicationConn(t, replicationConn)
err = replicationConn.CreateReplicationSlot("pgx_test", "test_decoding")
if err != nil {
t.Logf("replication slot create failed: %v", err)
t.Fatalf("replication slot create failed: %v", err)
}
// Do a simple change so we can get some wal data