From da23dd12f3ec7fd9ee26eaaa3dd7dee525dd5965 Mon Sep 17 00:00:00 2001 From: Feike Steenbergen Date: Fri, 6 Nov 2015 14:59:48 +0100 Subject: [PATCH] Do not wait for leader key to change after failover. Previously, the leader key was watched for changes after a failover. This resulted in a delay of up to 10 seconds to report a healthy failover back to the client. With this patch, we are not relying on the role of a member registered in the dcs anymore. --- patroni/cli.py | 21 +++++++++++---------- tests/test_ha.py | 4 ++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/patroni/cli.py b/patroni/cli.py index abd3b815..f11d0587 100644 --- a/patroni/cli.py +++ b/patroni/cli.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 ''' Patroni Command Line Client ''' @@ -182,7 +181,7 @@ def wait_for_master(dcs, timeout=30): dcs.watch(timeout) cluster = dcs.get_cluster() - if cluster.leader and cluster.leader.member.data['role'] == 'master': + if cluster.leader: return cluster raise Exception('Timeout occured') @@ -269,8 +268,13 @@ def failover(config_file, cluster_name, master, candidate, force, dcs): raise Exception('Member {} is not the leader of cluster {}'.format(master, cluster_name)) candidate_names = [str(m.name) for m in cluster.members if m.name != master] + ## We sort the names for consistent output to the client candidate_names.sort() + if len(candidate_names) == 0: + raise Exception('No candidates found to failover to') + + if candidate is None and not force: candidate = click.prompt('Candidate '+str(candidate_names), type=str, default='') @@ -306,10 +310,10 @@ def failover(config_file, cluster_name, master, candidate, force, dcs): click.echo(timestamp()+' Could not failover using Patroni api, falling back to DCS') dcs.set_failover_value(failover_value) click.echo(timestamp()+' Initialized failover from master {}'.format(master)) + # The failover process should within a minute update the failover key, we will keep watching it until it changes + # or we timeout + cluster = wait_for_master(dcs, timeout=60) - # The failover process should within a minute update the failover key, we will keep watching it until it changes - # or we timeout - cluster = wait_for_master(dcs, timeout=60) click.echo(timestamp()+' Failover completed in {:0.1f} seconds, new leader is {}'.format( time.time() - t_started, str(cluster.leader.member.name))) output_members(cluster, name=cluster_name) @@ -330,13 +334,10 @@ def output_members(cluster, name=None, format='pretty'): leader = '' if m.name == leader_name: leader = '*' - role = m.data['role'] - else: - role = 'replica' - rows.append([name, m.name, role, leader]) + rows.append([name, m.name, leader]) - print_output(['Cluster', 'Member', 'Role', 'Leader'], rows, {'Cluster': 'l', 'Member': 'l', 'Role': 'l'}, format) + print_output(['Cluster', 'Member', 'Leader'], rows, {'Cluster': 'l', 'Member': 'l'}, format) @cli.command('list', help='List the Patroni members for a given Patroni') diff --git a/tests/test_ha.py b/tests/test_ha.py index b775f4ee..e34f9b8e 100644 --- a/tests/test_ha.py +++ b/tests/test_ha.py @@ -27,10 +27,10 @@ def get_cluster_not_initialized_without_leader(): def get_cluster_initialized_without_leader(leader=False, failover=None): m = Member(0, 'leader', 28, {'conn_url': 'postgres://replicator:rep-pass@127.0.0.1:5435/postgres', - 'api_url': 'http://127.0.0.1:8008/patroni', 'role':'replica'}) + 'api_url': 'http://127.0.0.1:8008/patroni'}) l = Leader(0, 0, m) if leader else None o = Member(0, 'other', 28, {'conn_url': 'postgres://replicator:rep-pass@127.0.0.1:5436/postgres', - 'api_url': 'http://127.0.0.1:8011/patroni', 'role':'replica'}) + 'api_url': 'http://127.0.0.1:8011/patroni'}) return get_cluster(True, l, [m, o], failover)