pgbouncemgr/pgbouncemgr/state_store.py

76 lines
2.9 KiB
Python

# -*- coding: utf-8 -*-
# no-pylint: disable=missing-docstring,broad-except,too-many-instance-attributes
import json
from datetime import datetime
from os import rename
from os.path import isfile
from pgbouncemgr.logger import format_ex
from pgbouncemgr.state import \
UnknownLeaderNodeId, ActivePgbouncerConfigDoesNotExist
class StateStoreException(Exception):
"""Used for all exceptions that are raised from pgbouncemgr.state_store."""
class StateStore():
def __init__(self, path, state):
self.path = path
self.state = state
self.load()
def load(self):
"""Load state from the state file into the state object.
When this fails, an exception will be thrown.
Note that not all of the stored state is restored.
Only those parts that are required to be carried
on between restarts."""
# When no state file exists, there is no state to restore.
# Keep the state object as-is.
if not isfile(self.path):
return
# Otherwise, read the state from the state file.
with open(self.path, 'r') as stream:
try:
loaded_state = json.load(stream)
except json.decoder.JSONDecodeError:
# JSON decoding failed. This is beyond repair.
# Start with a clean slate to have the state data reinitialized.
return
# Copy the state over to the state object.
for key in [
"system_id",
"timeline_id",
"active_pgbouncer_config",
"leader_node_id"]:
if key in loaded_state:
try:
setattr(self.state, key, loaded_state[key])
except (UnknownLeaderNodeId,
ActivePgbouncerConfigDoesNotExist):
# These exception can occur when the node configuration
# has changed and some fields are no longer value.
# In such case, we don't update the attributes here
# and simply ignore the specific errors. After the
# application has started, the correct values will be
# filled by the manager process.
pass
def store(self):
"""Store the current state in the state file. Returns True when
the state was stored successfully. Returns False when it failed.
The error can be found in the err property."""
new_state = json.dumps(self.state.export(), sort_keys=True, indent=2)
try:
swap_path = "%s..SWAP" % self.path
with open(swap_path, "w") as file_handle:
print(new_state, file=file_handle)
rename(swap_path, self.path)
except Exception as exception:
raise StateStoreException(
"Storing state to file (%s) failed: %s" % (
self.path, format_ex(exception)))