Commit Graph
100 Commits
Author SHA1 Message Date
Alexander KukushkinandGitHub 1530ed0b9c Switch to GH actions (#1778)
it allows up to 20 parallel builds
2020-12-04 21:52:34 +01:00
Alexander KukushkinandGitHub e8e87bf0a1 Don't interrupt restart or promote if lost leader lock in pause (#1726)
In pause it is allowed to run postgres as master without lock.
2020-10-08 08:56:53 +02:00
Alexander KukushkinandGitHub 311e5ccc5b Release 2.0.1 (#1722)
* Bump version
* Update release notes
2020-10-01 15:18:53 +02:00
Alexander KukushkinandGitHub fa88d80c4f Apply master_start_timeout when executing crash recovery (#1720)
It is not very common, but the master Postgres might "crash" due to different reasons, like OOM, or out of disk space. Of course, there are chances that the current node holds some unreplicated data and therefore Patroni by default prefers to start Postgres on the leader node rather than doing a failover.

In order to be on the safe side Patroni always starts Postgres in recovery no matter whether the current node owns the leader lock or not. If the Postgres wasn't shut down cleanly, starting in recovery might fail, therefore in some cases as a workaround Patroni is executing a crash recovery by starting the postgres up in the single-user mode.

A few times we end up in the situation:
1. Master postgres crashed due to the out of disk space
2. Patroni starts crash recovery in a single-user mode
3. While doing crash-recovery Patroni keeps updating the leader lock

It makes Patroni stuck on step 3 and the manual intervention is required for recovering the cluster.

Patroni already has the option `master_start_timeout`, which controls for how long we let postgres stay in the `starting` state and after that Patroni might decide to release the leader lock if there are healthy replicas available which could take it over.

This PR makes the `master_start_timeout` option also work for crash recovery.
2020-09-30 08:04:27 +02:00
Alexander KukushkinandGitHub 2c5d62bf10 Workaround unittest bug and fix requirements (#1718)
* unittest bug: https://bugs.python.org/issue25532
* `urllib3[secure]` wrongly depends on `ipaddress` for python3, while in fact we don't need all dependencies of the `secure` extra, but only `ipaddress` for the `kubernetes` on python2.7 

Close https://github.com/zalando/patroni/issues/1717
Close https://github.com/zalando/patroni/issues/1709
2020-09-29 15:15:58 +02:00
Alexander KukushkinandGitHub 885d226dac Add support of raft bind_add and password (#1713)
Close https://github.com/zalando/patroni/issues/1705
2020-09-28 11:05:07 +02:00
Alexander KukushkinandGitHub fa6c396589 Fix bug in the get_guc_value() (#1712)
The `-D` parameter was forgotten.
2020-09-23 13:33:54 +02:00
Alexander KukushkinandGitHub 8a8409999d Change the behavior in pause (#1687)
1. Don't call bootstrap if PGDATA is missing/empty, because it might be for purpose, and someone/something working on it.
2. Consider postgres running as a leader in pause not healthy if pg_control sysid doesn't match with the /initialize key (empty initialize key will allow the "race" and the leader will "restore" initialize key).
3. Don't exit on sysid mismatch in pause, only log a warning.
4. Cover corner cases when Patroni started in pause with empty PGDATA and it was restored by somebody else
5. Empty string is a valid `recovery_target`.
2020-09-18 08:25:00 +02:00
Alexander KukushkinandGitHub 6706decc1c Fix hanging patronictl when RAFT is being used (#1697)
Close #1694
2020-09-16 14:03:40 +02:00
Alexander KukushkinandGitHub 83f9a031b8 Update issue templates (#1678)
We want to avoid ping-ping as much as possible.
2020-09-16 13:51:20 +02:00
Alexander KukushkinandGitHub 4dd902fbf1 Fix bug in kubernetes.update_leader (#1685)
Unhandled exception prevented demoting the primary.
In addition to that wrap the update_leader call in the HA loop into try..except block and implement a test case.

Fixes https://github.com/zalando/patroni/issues/1684
2020-09-11 10:19:03 +02:00
Alexander KukushkinandGitHub 0a1f389686 Release 2.0.0 (#1680)
* update release notes
* bump version
* change the default alignment in patronictl table output to `left`
* add missing tests
* add missing pieces to the documentation
2020-09-02 15:35:04 +02:00
Alexander KukushkinandGitHub 13e24d832d Advanced validation of PostgreSQL parameters (#1674)
So far Patroni was performing a comparison of the old value (in the `pg_settings`) with the new value (from Patroni configuration or from DCS) in order to figure out if reload or restart is required when the parameter has been changed. If the given parameter was missing in the `pg_settings` Patroni was ignoring it and not writing into the `postgresql.conf`.

In case if Postgres is not running, no validation has been performed and parameters and values were written into the config as it is.

It is not a very common mistake, but people tend to mistype parameter names or values.
Also, it happens that some parameters are removed in specific Postgres versions and some new are added (e.g. `checkpoint_segments` replaced with `min_wal_size` and `max_wal_size` in 9.5 or` wal_keep_segments` was replaced with `wal_keep_size` in 13).

Writing nonexistent parameters or invalid values into the `postgresql.conf` makes postgres unstartable.
This change doesn't solve the issue 100%, but at least approaching this goal very close.
2020-09-01 16:26:57 +02:00
Alexander KukushkinandGitHub 3e553df69d BUGFIX: pause on K8s (#1659)
On K8s the `Cluster.leader` is a valid object even if the cluster has no leader because we need to know the `resourceVersion` for future CAS operation. Such a non-empty object broke HA loop and made other nodes to think that the leader is there.

The right way to identify the missing leader which reliably works across all DCS is checking that the leader's name is empty.
2020-08-24 16:35:46 +02:00
Alexander KukushkinandGitHub 7bf60b64b0 Compatibility with PostgreSQL 13 (#1654)
So far Patroni was enforcing the same value of `wal_keep_segments` on all nodes in the cluster. If the parameter was missing from the global configuration it was using the default value `8`.
In pg13 beta3 the `wal_keep_segments` was renamed to the `wal_keep_size` and it broke Patroni.

If `wal_keep_segments` happened to be present in the configuration for pg13, Paroni will recalculate the value to `wal_keep_size` assuming that the `wal_segment_size` is 16MB. Sure, it is possible to get the real value of `wal_segment_size` from pg_control, but since we are dealing with the case of misconfiguration it is not worse time spend on it.
2020-08-17 10:45:02 +02:00
Alexander KukushkinandGitHub 23dcfaab49 Make it possible to bypass kubernetes service (#1614)
When running on K8s Patroni is communicating with API via the `kubernetes` service, which is address is exposed via the
`KUBERNETES_SERVICE_HOST` environment variable. Like any other service, the `kubernetes` service is handled by `kube-proxy`, that depending on configuration is either relying on userspace program or `iptables` for traffic routing.

During K8s upgrade, when master nodes are replaced, it is possible that `kube-proxy` doesn't update the service configuration in time and as a result Patroni fails to update the leader lock and demotes postgres.

In order to improve the user experience and get more control on the problem we make it possible to bypass the `kubernetes` service and connect directly to API nodes.
The strategy is very simple:
1. Resolve list IPs of API nodes from the kubernetes endpoint on every iteration of HA loop.
2. Stick to one of these IPs for API requests
3. Switch to a different IP if connected to IP is not from the list
4. If the request fails, switch to another IP and retry

Such a strategy is already used for Etcd and proven to work quite well.

In order to enable the feature, you need either to set to `true` `kubernetes.bypass_api_service` in the Patroni configuration file or `PATRONI_KUBERNETES_BYPASS_API_SERVICE` environment variable.

If for some reason `GET /default/endpoints/kubernetes` isn't allowed Patroni will disable the feature.
2020-08-14 12:39:47 +02:00
Alexander KukushkinandGitHub a6faf9b2d9 Refactor docker-compose.yml for better compatibility with new version (#1641)
The newest versions of docker-compose want to have some values double-quoted in the env file while old versions failing to process such files.
The solution is simple, move some of the parameters to the `docker-compose.yml` and rely on anchors for inheritance.
Since the main idea behind env files was to keep "secret" information off the main YAML we also get rid of any non-secret stuff, mainly located in the etcd.env.
2020-08-11 09:31:49 +02:00
Alexander KukushkinandGitHub a9915fb3c9 Explicitly disallow patching non-existent config (#1639)
For DCS other than `kubernetes` it was failing with exception due to the `cluster.config` being `None`, but on Kubernetes it was happily creating the config annotation and preventing writing bootstrap configuration after the bootstrap finished.
2020-08-07 09:36:56 +02:00
Alexander KukushkinandGitHub f1c6b0bebe Windows compatibility fixes (#1633)
* pg_rewind error messages contain '/' as directory separator
* fix Raft unit tests on win
* fix validator unit tests on win
* fix keepalive unit tests on win
* make standby cluster behave tests less shaky
2020-07-31 15:43:50 +02:00
Alexander KukushkinandGitHub 3341c898ff Add Etcd v3 protocol support via api gRPC-gateway (#1162)
The only python-etcd3 client working directly via gRPC still supports only a single endpoint, which is not very nice for high-availability.

Since Patroni is already using a heavily hacked version of python-etcd with smart retries and auto-discovery out-of-the-box, I decided to enhance the existing code with limited support of v3 protocol via gRPC-gateway.

Unfortunately, watches via gRPC-gateway requires us to open and keep the second connection to the etcd.

Known limitations:
* The very minimal supported version is 3.0.4. On earlier versions transactions don't work due to bugs in grpc-gateway. Without transactions we can't do atomic operations, i.e. leader locks.
* Watches work only starting from 3.1.0
* Authentication works only starting from 3.3.0
* gRPC-gateway does not support authentication using TLS Common Name. This is because gRPC-proxy terminates TLS from its client so all the clients share a cert of the proxy: https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/authentication.md#using-tls-common-name
2020-07-31 14:33:40 +02:00
Alexander KukushkinandGitHub b3c69b5fb2 Fix unit tests (#1630)
exception raised from `logging` module was breaking pytest
2020-07-30 10:33:31 +02:00
Alexander KukushkinandGitHub bfbc4860d5 PoC: Patroni on pure RAFT (#375)
* new node can join the cluster dynamically and become a part of consensus
 * it is also possible to join only Patroni cluster (without adding the node to the raft), just comment or remove `raft.self_addr` for that
 * when the node joins the cluster it is using values from `raft.partner_addrs` only for initial discovery.
* It is possible to run Patroni and Postgres on two nodes plus one node with `patroni_raft_controller` (without Patroni and Postgres). In such setup one can temporarily lose one node without affecting the primary.
2020-07-29 15:34:44 +02:00
Alexander KukushkinandGitHub 59dae9b1bb A few little bug-fixes (#1623)
1. Put the `*` character into pgpass if the actual value is empty
2. Re-raise fatal exception from the HA loop (we need to exit if for example cluster initialization failed)

Close https://github.com/zalando/patroni/issues/1617
2020-07-28 08:37:04 +02:00
Alexander KukushkinandGitHub 38f5f464cb Compatibility fixes (#1616)
* socket.TCP_KEEP* are not defined on windows, macosx, and *bsd
* add [secure] extra to urllib3 for better handling of ip/host checks
2020-07-21 08:13:44 +02:00
Alexander KukushkinandGitHub a68692a3e4 Get rid of kubernetes python module (#1586)
The official python kubernetes client contains a lot of auto-generated code and therefore very heavy, but we need only a little fraction of it.
The naive implementation, that covers all API methods we use, takes about 250 LoC, and about half of it is responsible for the handling of configuration files.

Disadvantage: If somebody was using the `patronictl` outside of the pod (on his machine), it might not work anymore (depending on the environment).
2020-07-17 08:31:58 +02:00
Alexander KukushkinandGitHub ba15806592 Bugfix: Patroni falsely reported postgres as running (#1612)
If postgres is not running due to the crash, panic or because it was stopped by some external force, we need to change the internal state.
So far we always relied on the state being set in the `Postgresql.start()` method. Unfortunately not in all cases this method is reachable. For example, the `Postgresql.follow()` could raise an exception when calling `write_recovery_conf()` and there is not enough disk space to write the file.

In order to solve it, we explicitly set the state when detected that the postgres is not alive. In the `pause` we set it to `stopped`, otherwise to `crashed` if it was `running` or `starting` before.
2020-07-15 10:38:26 +02:00
Alexander KukushkinandGitHub 04b9fb9dd4 Make sure cached last_leader_operation is up-to-date on replicas (#1600)
Patroni is caching the cluster view in the DCS object because not all operations require the most up-to-date values. The cached version is valid for TTL seconds. So far it worked quite well, the only known problem was that the `last_leader_operation` for some DCS implementations was not very up-to-date:

* Etcd: since the `/optime/leader` key is updated right after the `/leader` key, usually all replicas get the value from the previous HA loop. Therefore the value is somewhere between `loop_wait` and `loop_wait*2` old. We improve it by using the 10ms artificial sleep after receiving watch notification from `compareAndSwap` operation on the leader key. It usually gives enough time for the primary to update the `/optime/leader`. On average that makes the cached version `loop_wait/2` old.

* ZooKeeper: Patroni itself is not so much interested in most up-to-date values of member and leader/optime ZNodes. In case of the leader race it just reads everything from ZooKeeper, but during normal operation it is relying on cache. In order to see the recent value on replicas they are doing watch on the `leader/optime` Znode and will re-read it after it was updated by the primary. On average that makes the cached version `loop_wait/2` old.

* Kubernetes: last_leader_operation is stored in the same object as the leader key itself and therefore update is atomic and we always see the latest version. That makes the cached version `loop_wait/2` old on avg.

* Consul: HA loops on the primary and replicas are not synchronized, therefore at the moment when we read the cluster state from the Consul KV we see the last_leader_operation value that is between 0 and loop_wait old. On average that makes the cached version `loop_wait` old. Unfortunately we can't make it much better without performing periodic updates from Consul, which might have negative side effects.

Since the `optime/leader` is only updated at most once per HA loop cycle, the value stored in the DCS is usually `loop_wait/2` old on avg. For majority of DCS implementations we could promise that the cached version in Patroni will match the value in DCS most of the time, therefore there is no need to make additional requests. The only exception is Consul, but probably we could just document it, so when someone relying on last_leader_operation value to check the replication lag can correspondingly adjust thresholds.

Will help to implement #1599
2020-07-15 10:31:32 +02:00
Alexander KukushkinandGitHub db8c634db3 Create readiness and liveness endpoints (#1590)
They could be useful to eliminate "unhealthy" pods from subsets addresses when the K8s service with label selectors are used.
Real-life example: the node where the primary was running has failed and being shutdown and Patroni can't update (remove) the role label.
Therefore on OpenShift the leader service will have two pods assigned, one of them is a failed primary.
With the readiness probe defined, the failed primary pod will be excluded from the list.
2020-07-10 14:08:39 +02:00
Alexander KukushkinandGitHub 7a13579973 Refactor tcp_keepalive code (#1578)
* Move it into a separate function
* set keepalive on the REST API socket

The function will be also used in #1162
2020-07-08 14:04:59 +02:00
Alexander KukushkinandGitHub 8eb01c77b6 Don't fire on_reload when promoting to standby_leader on 13+ (#1552)
PostgreSQL 13 finally introduced the possibility to change the `primary_conninfo` without a restart. Just doing reload is enough, but in case if the role is changing from the `replica` to the `standby_leader` we want to call only `on_role_change` callback and skip `on_reload`, because they duplicate each other.
2020-06-29 14:49:25 +02:00
Alexander KukushkinandGitHub cbff544b9c Implement patronictl flush switchover (#1554)
It includes implementing the `DELETE /switchover` REST API endpoint.

Close https://github.com/zalando/patroni/issues/1376
2020-06-25 16:27:57 +02:00
Alexander KukushkinandGitHub 7f343c2c57 Try to fetch missing WAL if pg_rewind complains about it (#1561)
It could happen that the WAL segment required for `pg_rewind` doesn't exist in the `pg_wal` anymore and therefore `pg_rewind` can't find the checkpoint location before the diverging point.
Starting from PostgreSQL 13 `pg_rewind` could use `restore_command` for fetching missing WALs, but we can do better than that.
On older PostgreSQL versions Patroni will parse the stdout and stderr of failed rewind attempt, try to fetch the missing WAL by calling the `restore_command`, and repeat an attempt.
2020-06-25 16:24:21 +02:00
Alexander KukushkinandGitHub e00acdf6df Fix possible race conditions in update_leader (#1596)
1. Between get_cluster() and update_leader() calls the K8s leader object might be updated from outside and therefore the resource version will not match (error code=409). Since we are watching for all changes, the ObjectCache likely will have the most up-to-date version and we will take advantage of that. There is still a chance to hit a race-condition, but it would be smaller than before. Actually, other DCS are free of this issue. Etcd - update is based on the value comparison, Zookeeper and Consul are relying on session mechanism.
2. If the update still failed - recheck the resource version of the leader object and that the current node is still the leader there and repeat the call.

P.S. The leader race is still relying on the version of the leader object as it was during the get_cluster() call.

In addition to that fixed handling of K8s API errors, we should retry on 500, not on 502.
Close https://github.com/zalando/patroni/issues/1589
2020-06-22 16:07:52 +02:00
Alexander KukushkinandGitHub ee4bf79c11 Populate references and nodename in subsets addresses (#1591)
It makes subsets to exactly look like they were populated by the service with label selector and would help with https://github.com/zalando/postgres-operator/issues/340#issuecomment-587001109

Unit-tests are refactored to minimize amount of mocks.
2020-06-16 12:56:20 +02:00
Alexander KukushkinandGitHub e95e54b94e Handle correctly health-checks for standby cluster (#1553)
Close https://github.com/zalando/patroni/issues/1388
2020-06-05 10:37:02 +02:00
Alexander KukushkinandGitHub 4f1a3e53cd Defer TLS handshake until thread has started (#1547)
The `SSLSocket` is immediately doing the handshake on accept. Effectively it blocks the whole API thread if the client-side doesn't send any data.
In order to solve the issue we defer the handshake until a thread serving request has started.

The solution is a bit hacky, but thread-safe.

Close https://github.com/zalando/patroni/issues/1545
2020-06-05 09:36:13 +02:00
Alexander KukushkinandGitHub 1229cf2c16 Ignore hba_file and ident_file when they match with defaults (#1555)
It is possible to specify custom hba_file and ident_file in the postgresql configuration parameters and Patroni is considering that these files are managed externally. It could happen that locations of these files matching with default locations of pg_hba,conf and pg_ident.conf. In this case we will ignore custom values and fallback to the default workflow, i.e. Patroni will overwrite them.

Close: https://github.com/zalando/patroni/issues/1544
2020-06-05 09:33:50 +02:00
Alexander KukushkinandGitHub 1b2491cedf Check basic-auth indepandantly from client certificate (#1556)
this is absolutely valid use-case
2020-06-05 09:25:33 +02:00
Alexander KukushkinandGitHub 80ed08a2bb Enforce synchronous_commit=local for post_init script (#1559)
Patroni was already doing that before creating users for a long time, but the post_init was an oversight. It will help to all utilities relying on libpq and reduce the end-user confusion.
2020-06-05 09:24:47 +02:00
Alexander KukushkinandGitHub c2a78ee652 Bugfix: GET /cluster was showing stale member info in zookeeper (#1573)
Zookpeeper implementation heavily relies on cached version of the cluster view in order to minimize the number of requests. Having stale members information is fine for Patroni workflow because it basically relies only on member names and tags.

The `GET /cluster` is a different case. Being exposed outside it might be used for monitoring purposes and therefore we should show the up-to-date members information.
2020-06-05 09:23:54 +02:00
Alexander KukushkinandGitHub 76cfcf3ae8 Don't rely on deprecated flake8 setuptools entrypoint (#1557)
Define and use own command class for that
2020-06-03 09:54:04 +02:00
Alexander KukushkinandGitHub cd1b2741fa Improve timeline divergence check (#1563)
We don't need to rewind when:
1. replayed location for the former replica is not ahead of switchpoint
2. end of checkpoint record for the former primary is the same as switchpoint

In order to get the end of checkpoint record we use the `pg_waldump` and parse its output.

Close https://github.com/zalando/patroni/issues/1493
2020-05-29 14:15:10 +02:00
Alexander KukushkinandGitHub 98c2081c67 Detect a new timeline in the standby cluster (#1522)
The standby cluster doesn't know about leader elections in the main cluster and therefore the usual mechanisms of detecting divergences don't work. For example, it could happen that the standby cluster is ahead of the new primary of the main cluster and must be rewound.
There is a way to know that the new timeline has been created by checking the presence of a history file in pg_wal. If the new file is there, we will start usual procedures of making sure that we can continue streaming or will run the pg_rewind.
2020-05-29 14:14:47 +02:00
Alexander KukushkinandGitHub c6207933d1 Properly handle the exception raised from refresh_session (#1531)
The `touch_member()` could be called from the finally block of the `_run_cycle()`. In case if it raised an exception the whole Patroni process was crashing.
In order to avoid future crashes we wrap `_run_cycle()` into the try..except block and ask a user to report a BUG.

Close https://github.com/zalando/patroni/issues/1529
2020-05-29 14:14:11 +02:00
Alexander KukushkinandGitHub 6a0d2924a0 Separate received and replayed location (#1514)
When making a decision whether the running replica is able to stream from the new primary or must be rewound we should use replayed location, therefore we extract received and replayed independently.

Reuse the part of the query that extracts the timeline and locations in the REST API.
2020-05-27 13:33:37 +02:00
Alexander KukushkinandGitHub 881bba9e1c Sync HA loops of all pods in one cluster (#1515)
There is no expire mechanism available on K8s, therefore we implement soft leader lock, i.e. every pod is "watching" for changes of the leader object and when there are no changes during the TTL it starts leader race.

Before we switched to LIST+WATCH approach in #1189 and #1276, we only watched for the leader object and every time it was updated, the main thread of the HA loop was waking up. As a result, all replica pods were synchronized, and starting the leader race more or less at the same time.

The new approach made all pods "unsynchronized" and the biggest downside of it - it takes `ttl + loop_wait` in the worst case to detect the leader failure.

This commit makes all pods in one cluster to sync HA loops again based on updates of the leader object.
2020-05-15 18:04:59 +02:00
Alexander KukushkinandGitHub ad5c686c11 Take advantage of pg_stat_wal_recevier (#1513)
So far Patroni was parsing `recovery.conf` or querying `pg_settings` in order to get the current values of recovery parameters. On PostgreSQL earlier than 12 it could easily happen that the value of `primary_conninfo` in the `recovery.conf` has nothing to do with reality. Luckily for us, on PostgreSQL 9.6+ there is a `pg_stat_wal_receiver` view, which contains current values of `primary_conninfo` and `primary_slot_name`. The password field is masked through, but this is fine, because authentication happens only during opening the connection. All other parameters we compare as usual.

Another advantage of `pg_stat_wal_recevier` - it contains the current timeline, therefore on 9.6+ we don't need to use the replication connection trick if walreceiver process is alive.

If there is no walreceiver process available or it is not streaming we will stick to old methods.
2020-05-15 18:04:24 +02:00
Alexander KukushkinandGitHub 08b3d5d20d Move ensure_clean_shutdown into rewind module (#1528)
Logically fits there better
2020-05-15 16:22:57 +02:00
Alexander KukushkinandGitHub 30aa355eb5 Shorten and beautify history log output (#1526)
when Patroni is trying to figure out the necessity of pg_rewind it could write the content history file from the primary into the log. The history file is growing with every failover/switchover and eventually starts taking too many lines in the log, most of them are not so much useful.
Instead of showing the raw data, we will show only 3 lines before the current replica timeline and 2 lines after.
2020-05-15 16:14:25 +02:00
Alexander KukushkinandGitHub 7cf0b753ab Update optime/leader with checkpoint location after clean shut down (#1527)
Potentially this information could be used in order to make sure that there is no data loss on switchover.
2020-05-15 16:13:16 +02:00
Alexander KukushkinandGitHub 285bffc68d Use pg_rewind with --restore-target-wal on 13 if possible (#1525)
On PostgreSQL 13 check if restore_command is configured and tell pg_rewind to use it
2020-05-15 16:05:07 +02:00
Alexander KukushkinandGitHub e6ef3c340a Wake up the main thread after checkpoint is done (#1524)
Replicas are waiting for checkpoint indication via member key of the leader in DCS. The key is normally updated only one time per HA loop.
Without waking the main thread up replicas will have to wait up to `loop_wait` seconds longer than necessary.
2020-05-15 16:02:17 +02:00
Alexander KukushkinandGitHub 0d957076ca Improve compatibility with PostgreSQL 12 and 13 (#1523)
There were two new connection parameters introduced:
1. `gssencmode` in 12
2. `channel_binding` in 13
2020-05-13 13:13:25 +02:00
Alexander KukushkinandGitHub a6fbc2dd7b Handle the case when member conn_url is missing (#1510)
Close https://github.com/zalando/patroni/issues/1508
2020-05-13 12:26:39 +02:00
Alexander KukushkinandGitHub 703a129646 Don't try calling a non existing leader in patronictl pause (#1542)
While pausing a cluster without a leader on K8s patronictl was showing warnings that member "None" could not be accessed.
2020-05-13 12:22:47 +02:00
Alexander KukushkinandGitHub fe23d1f2d0 Release 1.6.5 (#1503)
* bump version
* update release notes
* implement missing unit-tests and format code.
2020-04-23 16:02:01 +02:00
Alexander KukushkinandGitHub be4c078d95 Etcd smart refresh members (#1499)
In dynamic environments it is common that during the rolling upgrade etcd nodes are changing their IP addresses. If the etcd node where Patroni is currently connected to is upgraded last, it could happen that the cached topology doesn't contain any live node anymore and therefore request can't be retried and totally fails, usually resulting in demoting of the primary.

In order to partially overcome the problem, Patroni is already doing a periodic (every 5 minutes) rediscovery of the etcd cluster topology, but in case of very fast node rotation there was still a possibility to hit the issue.

This PR is an attempt to address the problem. If the list of nodes exhausted, Patroni will try to perform initial discovery via an external mechanism, like resolving A or SRV dns records and if the new list is different from the original, Patroni will use it as the new etcd cluster topology.

In order to deal with tcp issues the connect_timeout is set to max(read_timeout/2, 1). It will make list of members exhaust faster, but leaves the time to perform topology rediscovery and another attempt.

The third issue addressed by this PR - it could happen that dns names of etcd nodes didn't change, but ip addresses are new, therefore we clean up the internal dns cache when doing topology rediscovery.

Besides that, this commit makes `_machines_cache` property pretty much static, it will be updated only when the topology has changed and helps to avoid concurrency issues.
2020-04-23 12:51:05 +02:00
Alexander KukushkinandGitHub 80fbe90056 Issue CHEKPOINT explicitely after promote happened (#1498)
It is safe to call pg_rewind on the replica only when pg_control on the primary contains information about the latest timeline. Postgres is usually doing immediate checkpoint right after promote and in most cases it works just fine. Unfortunately we regularly receive complaints that it takes to long (minutes) until the checkpoint is done and replicas can't perform rewind. At the same time doing the checkpoint manually immediately helped. So Patroni starts doing the same. When the promotion happened and postgres is not running in recovery, we explicitly issue the checkpoint.

We are intentionally not using the AsyncExecutor here, because we want the HA loop continues doing its normal flow.
2020-04-20 11:55:05 +02:00
Alexander KukushkinandGitHub 337f9efc9e Improve patronictl list output (#1486)
The redundant column `Column` will be presented in the table header.

Depending on output format `Tags` are serialized differently:
* For *pretty* format YAML is used, every element on the new line
* For *tsv* format for YAML is also used, but all elements and on the same line (similar to JSON)
* For *json* and *yaml* formats `Tags` are serialized into an appropriate format.

<details><summary>Examples of output in pretty formats:</summary>

```bash
$ patronictl list
+ Cluster: batman (6813309862653668387) +---------+----+-----------+---------------------+
|    Member   |      Host      |  Role  |  State  | TL | Lag in MB | Tags                |
+-------------+----------------+--------+---------+----+-----------+---------------------+
| postgresql0 | 127.0.0.1:5432 | Leader | running |  3 |           | clonefrom: true     |
|             |                |        |         |    |           | noloadbalance: true |
|             |                |        |         |    |           | nosync: true        |
+-------------+----------------+--------+---------+----+-----------+---------------------+
| postgresql1 | 127.0.0.1:5433 |        | running |  3 |       0.0 |                     |
+-------------+----------------+--------+---------+----+-----------+---------------------+

$ patronictl list badclustername
+ Cluster: badclustername (uninitialized) ------+
| Member | Host | Role | State | TL | Lag in MB |
+--------+------+------+-------+----+-----------+
+--------+------+------+-------+----+-----------+
```
</details>

<details><summary>Example in tsv format:</summary>

```bash
Cluster Member  Host    Role    State   TL      Lag in MB       Pending restart Tags
batman  postgresql0     127.0.0.1:5432  Leader  running 2
batman  postgresql1     127.0.0.1:5433          running 2       0               {clonefrom: true, nofailover: true, noloadbalance: true, replicatefrom: postgresql0}
batman  postgresql2     127.0.0.1:5434          running 2       0       *       {replicatefrom: postgres1}
```
</details>

In addition to that, `patronictl list` command will stop showing keys with empty values in `json` and `yaml` formats.
<details><summary>Examples:</summary>

```yaml
$ patronictl list -f yaml
- Cluster: batman
  Host: 127.0.0.1:5432
  Member: postgresql0
  Role: Leader
  State: running
  TL: 2
- Cluster: batman
  Host: 127.0.0.1:5433
  Lag in MB: 0
  Member: postgresql1
  State: running
  TL: 2
  Tags:
    clonefrom: true
    nofailover: true
    noloadbalance: true
    replicatefrom: postgresql0
- Cluster: batman
  Host: 127.0.0.1:5434
  Lag in MB: 0
  Member: postgresql2
  Pending restart: '*'
  State: running
  TL: 2
  Tags:
    replicatefrom: postgres1
```

```json
$ patronictl list -f json | jq .
[
  {
    "Cluster": "batman",
    "Member": "postgresql0",
    "Host": "127.0.0.1:5432",
    "Role": "Leader",
    "State": "running",
    "TL": 2
  },
  {
    "Cluster": "batman",
    "Member": "postgresql1",
    "Host": "127.0.0.1:5433",
    "State": "running",
    "TL": 2,
    "Lag in MB": 0,
    "Tags": {
      "nofailover": true,
      "noloadbalance": true,
      "replicatefrom": "postgresql0",
      "clonefrom": true
    }
  },
  {
    "Cluster": "batman",
    "Member": "postgresql2",
    "Host": "127.0.0.1:5434",
    "State": "running",
    "TL": 2,
    "Lag in MB": 0,
    "Pending restart": "*",
    "Tags": {
      "replicatefrom": "postgres1"
    }
  }
]
```
</details>
2020-04-15 12:19:18 +02:00
Alexander KukushkinandGitHub 27cda08ece Improve unit-tests (#1479)
* tests were failing on windows and macos
* improve coverage
2020-04-09 10:34:35 +02:00
Alexander KukushkinandGitHub 369a93ce2a Handle cases when conn_url is not defined (#1482)
On K8s when one of the Patroni pods in starting there is valid annotation yet, which could cause failure in patronictl.
In addition to that handle cases if port isn't specified in the standby_cluster configuration.

Close https://github.com/zalando/patroni/issues/1100
Close https://github.com/zalando/patroni/issues/1463
2020-04-09 10:34:12 +02:00
Alexander KukushkinandGitHub e58680f833 Convert recovery_min_apply_delay to ms before doing comparison (#1484)
Fixes https://github.com/zalando/patroni/issues/1483
2020-04-09 10:33:38 +02:00
Alexander KukushkinandGitHub d2080a3116 Retry if the retry-after http header is set (#1431)
If the K8s API is overwhelmed with requests it might ask to retry.
2020-03-13 16:36:39 +01:00
Alexander KukushkinandGitHub d82301688f Fix pyinstaller compatibility (#1441)
Close https://github.com/zalando/patroni/issues/1440
2020-03-13 16:36:12 +01:00
Alexander KukushkinandGitHub b020874486 Small improvement in tests (#1423)
which actually revealed a small issue in the validator
2020-03-10 12:07:40 +01:00
Alexander KukushkinandGitHub ab38ab2e97 Apply 1 second backoff if LIST failed (#1424)
It is mostly necessary to avoid flooding logs, but also help to prevent starvation of the main thread.
2020-03-10 12:07:26 +01:00
Alexander KukushkinandGitHub 613634c26b Reset rewind state if postgres started after successful pg_rewind (#1408)
Close https://github.com/zalando/patroni/issues/1406
2020-02-27 12:24:17 +01:00
Alexander KukushkinandGitHub 4a29caa9d3 On role change callback didn't fire on failed primary (#1420)
Bug was introduced in https://github.com/zalando/patroni/pull/703
Close https://github.com/zalando/patroni/issues/1418
2020-02-27 12:22:44 +01:00
Alexander KukushkinandGitHub bcd75bbeeb Avoid opening replication connection on every cycle of HA loop (#1422)
Bug was introduces in the https://github.com/zalando/patroni/pull/1332
2020-02-27 12:22:11 +01:00
Alexander KukushkinandGitHub 80ce61876e Don't create permanent physical slot with name of the primary (#1392)
It is a regular issue that primary is recycling WALs when one of the replicas is down for a long time. So far there were only two solutions for such a problem and both of them are not perfect:
1. Increase `wal_keep_segments`, but it is hard to guess the good value.
2. Use continuous archiving and PITR, but it is not always possible.

This PR is introducing the way to solve the problem for static clusters, with a fixed number of nodes and names that never change. You just need to list the names of all nodes in the `slots` so the primary will not remove the slot when the node is down (not registered in DCS).
Of course, the primary will not create the permanent slot which is matching its own name.

Usage example: let's assume you have a cluster with nodes named *abc1*, *abc2*, and *abc3*.
You have to run `patronictl edit-config` and put the following snippet into the configuration:
```yaml
slots:
  abc1:
    type: physical
  abc2:
    type: physical
  abc3:
    type: physical
```

If the node *abc2* is the primary, it will always create slots for *abc1* and *abc3* even if they are not running, but will not create slot *abc2*.
Other nodes will behave the same.

Close #280
2020-02-20 10:07:43 +01:00
Alexander KukushkinandGitHub 4737af48bf Get rid of dependency on tzlocal module (#1390)
It was used only to add the local timezone to the datetime specified in the patronictl for scheduled switchover or restart.
The `dateutil.tz.tzlocal()` does the same job equally well.
2020-02-13 14:49:57 +01:00
Alexander KukushkinandGitHub dc1966e3bc Release 1.6.4 (#1380)
* Bump version
* Update release notes
2020-01-27 14:15:21 +01:00
Alexander KukushkinandGitHub 6aa3f809d4 Configure keepalive for connections to K8s API (#1366)
In case if we got nothing from the socket after the TTL seconds it should be considered dead.
2020-01-27 09:25:08 +01:00
Alexander KukushkinandGitHub 902411239f More compatibility with windows (#1367)
* unix-domain sockets are not yet supported
* signal.SIGQUIT doesn't exists
2020-01-24 12:52:55 +01:00
Alexander KukushkinandGitHub 0eb1e0568b Compatibility with python 3.7.6 (#1368)
The urlparse function has changed.

Old versions:
```python
>>> urlparse('localhost:8500')
ParseResult(scheme='', netloc='', path='localhost:8500', params='', query='', fragment='')
```

3.7.6:
```python
>>> urlparse('localhost:8500')
ParseResult(scheme='localhost', netloc='', path='8500', params='', query='', fragment='')
```
2020-01-24 12:52:34 +01:00
Alexander KukushkinandGitHub 27ff9dfda3 Avoid logging passwords on user creation (#1370)
Fixes https://github.com/zalando/patroni/issues/1365
2020-01-24 10:52:23 +01:00
Alexander KukushkinandGitHub 1c4d395d5a Handle exception from Ha.shutdown (#1351)
During the shutdown Patroni is trying to update its status in the DCS.
If the DCS is inaccessible an exception might be raised. Lack of exception handling prevents logger thread from stopping.

Fixes https://github.com/zalando/patroni/issues/1344
2020-01-16 14:34:58 +01:00
Alexander KukushkinandGitHub c5fc6fd936 Special handling of parameters with period (#1349)
It might be that they are defined by the extension and therefore the unit is not necessarily is the string.
It also could be that change of the value requires a restart (for example pg_stat_statements.max).
2020-01-16 14:34:31 +01:00
Alexander KukushkinandGitHub 1461d7d4b8 Allow certain recovery parameters be defined in the custom_conf (#1335)
Fixes https://github.com/zalando/patroni/issues/1333
2020-01-15 12:41:07 +01:00
Alexander KukushkinandGitHub 102a12ea5a Catch all exceptions when calling socket.getaddrinfo (#1355)
it serves two purposes:
1. We don't want accidentally break the thread
2. During the shutdown socket.gaierror become unresolvable and nasty exceptions are raised

Close: https://github.com/zalando/patroni/issues/1353
2020-01-15 12:38:34 +01:00
Alexander KukushkinandGitHub 9e8ae95bce Finally fix a problem with case sensitivity of sync standby names (#1359)
Close https://github.com/zalando/patroni/issues/1358
2020-01-15 12:38:05 +01:00
Alexander KukushkinandGitHub 16d1ffdde7 Update timeline on standby cluster (#1332)
Fixes https://github.com/zalando/patroni/issues/1031
2019-12-20 12:56:00 +01:00
Alexander KukushkinandGitHub a675fa18dc Handle case when replication password is set to null (#1330)
Fixes https://github.com/zalando/patroni/issues/1231
2019-12-20 12:06:18 +01:00
Alexander KukushkinandGitHub d941f6bc5e Use restore_command from standby_cluster config on cascading replicas (#1341)
The standby_leader was already doing it from the beginning feature existed. Not doing the same on replicas might prevent them from catching up with standby leader due to WALs being recycled.

In addition to that apply the same strategy to archive_cleanup_command.
2019-12-20 12:03:25 +01:00
Alexander KukushkinandGitHub 08d6e5e50e BUGFIX: don't leak password when running pg_rewind (#1321)
In addition to that:
* enforce security settings from `postgresql.authention`
* update release notes
* bump version
* close https://github.com/zalando/patroni/issues/1320
2019-12-05 18:19:38 +01:00
Alexander KukushkinandGitHub b542e4b5f0 Release 1.6.2 (#1319)
* update release notes
* bump version
2019-12-05 11:36:17 +01:00
Alexander KukushkinandGitHub 0693fe7dd0 Housekeeping (#1315)
* Reduce memory usage by patroni init process
* More cleanup in setup.py
* Implement missing tests
2019-12-04 11:28:46 +01:00
Alexander KukushkinandGitHub f1819443ef Avoid spawning semaphore tracker process (#1299)
We are not using semaphores, therefore we don't need to track them.
2019-12-02 12:16:18 +01:00
Alexander KukushkinandGitHub e1d569ad75 Inherit CaseInsensitiveDict from urllib3 HTTPHeaderDict (#1302)
It might look like a hack, but the API is stable enough and didn't change in the past 3+ years.
2019-12-02 12:14:59 +01:00
Alexander KukushkinandGitHub cc0df4900b Set User-Agent for all http requests (#1312)
Example: `Patroni/1.6.1 Python/3.6.8 Linux`
2019-12-02 10:46:20 +01:00
Alexander KukushkinandGitHub a5ff38a034 Improve behave tests (#1313)
Hopefully, make them less flaky
2019-12-02 10:33:44 +01:00
Alexander KukushkinandGitHub a3be2958a7 Tidy up setup.py (#1308)
1. Stop using `setuptools.command.test`, it is being deprecated
2. Remove junit integration
2019-11-27 15:56:50 +01:00
Alexander KukushkinandGitHub 85341ff78b Use passfile in primary_conninfo only on 10+ (#1301)
somehow passfile happened to work on ubuntu with older postgres versions, but it is not always the case for other distros.
2019-11-27 14:58:23 +01:00
Alexander KukushkinandGitHub 7793887ea7 Fix tests on windows (#1303)
and disable junit, it produces a deprecation warning
2019-11-27 14:57:33 +01:00
Alexander KukushkinandGitHub 525a26fab5 Solve the problem of cyclic imports (#1306)
Move `PATRONI_ENV_PREFIX` into the `patroni/__init__.py`
2019-11-26 17:03:34 +01:00
Alexander KukushkinandGitHub 90a4208390 Get rid from requests module (#1296)
It wasn't used for anything critical anyway, so it doesn't make a lot of sense to keep it as an explicit dependency.
2019-11-22 15:31:55 +01:00
Alexander KukushkinandGitHub f03b85f5b0 Fix calculation of wal_buffes (#1297)
Close https://github.com/zalando/patroni/issues/1288
2019-11-22 15:30:28 +01:00
Alexander KukushkinandGitHub 474ac3cc11 Move multiprocessing.set_start_method() back to main (#1295)
It is not possible to call it from forked process
2019-11-21 17:28:14 +01:00
Alexander KukushkinandGitHub 412c720d3a Avoid importing all DCS modules (#1286)
We will try to import only the module which has a configuration section.
I.e. if there is only zookeeper section in the config, Patroni will try to import only `patroni.dcs.zookeeper` and skip `etcd`, `consul`, and `kubernetes`.
This approach has two benefits:
1. When there are no dependencies installed Patroni was showing INFO messages `Failed to import smth`, which looks scary.
2. It reduces memory usage, because sometimes dependencies are heavy.
2019-11-21 14:39:37 +01:00
Alexander KukushkinandGitHub 183adb7848 Housekeeping (#1284)
* Implement proper tests for `multiprocessing.set_start_method()`
* Exclude some watchdog code from coverage (it is used only for behave tests)
* properly use os.path.join for windows compatibility
* import DCS modules in `features/environment.py` on demand. It allows to run behave tests against chosen DCS without installing all dependencies.
* remove some unused behave code
* fix some minor issues in the dcs.kubernetes module
2019-11-21 13:27:55 +01:00