76 lines
2.9 KiB
Python
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)))
|