pg-converter 2.0b0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pg_converter/__init__.py +21 -0
- pg_converter/__main__.py +14 -0
- pg_converter/api.py +114 -0
- pg_converter/bundled/__init__.py +0 -0
- pg_converter/bundled/conf/pg_converter.conf.example +54 -0
- pg_converter/bundled/packets/alert_bloat/01_step.sql +117 -0
- pg_converter/bundled/packets/alert_bloat/02_step.sql +73 -0
- pg_converter/bundled/packets/alert_bloat/meta_data.json +13 -0
- pg_converter/bundled/packets/alert_check_autovacuum/01_step.sql +18 -0
- pg_converter/bundled/packets/alert_check_autovacuum/meta_data.json +10 -0
- pg_converter/bundled/packets/alert_int4_capacity/01_gen_obj.sql +22 -0
- pg_converter/bundled/packets/alert_int4_capacity/01_step.sql +32 -0
- pg_converter/bundled/packets/alert_int4_capacity/meta_data.json +14 -0
- pg_converter/bundled/packets/alert_invalid_indexes/01_step.sql +8 -0
- pg_converter/bundled/packets/alert_invalid_indexes/meta_data.json +12 -0
- pg_converter/bundled/packets/alert_max_connections/01_step.sql +9 -0
- pg_converter/bundled/packets/alert_max_connections/meta_data.json +13 -0
- pg_converter/bundled/packets/alert_stat/01_step.sql +34 -0
- pg_converter/bundled/packets/alert_stat/meta_data.json +13 -0
- pg_converter/bundled/packets/alert_xact_age/01_step.sql +11 -0
- pg_converter/bundled/packets/alert_xact_age/02_step.sql +7 -0
- pg_converter/bundled/packets/alert_xact_age/meta_data.json +13 -0
- pg_converter/bundled/packets/dba_active_maint/01_step.sql +34 -0
- pg_converter/bundled/packets/dba_active_maint/meta_data.json +11 -0
- pg_converter/bundled/packets/dba_clone_schema/01_step.sql +433 -0
- pg_converter/bundled/packets/dba_clone_schema/meta_data.json +4 -0
- pg_converter/bundled/packets/dba_find_db/01_step.sql +1 -0
- pg_converter/bundled/packets/dba_find_db/meta_data.json +5 -0
- pg_converter/bundled/packets/dba_get_conf/01_step.sql +32 -0
- pg_converter/bundled/packets/dba_get_conf/meta_data.json +5 -0
- pg_converter/bundled/packets/dba_get_version/01_step.sql +1 -0
- pg_converter/bundled/packets/dba_get_version/meta_data.json +11 -0
- pg_converter/bundled/packets/dba_idx_diag/01_step.sql +21 -0
- pg_converter/bundled/packets/dba_idx_diag/02_step.sql +16 -0
- pg_converter/bundled/packets/dba_idx_diag/03_step.sql +16 -0
- pg_converter/bundled/packets/dba_idx_diag/04_step.sql +23 -0
- pg_converter/bundled/packets/dba_idx_diag/05_step.sql +29 -0
- pg_converter/bundled/packets/dba_idx_diag/06_step.sql +46 -0
- pg_converter/bundled/packets/dba_idx_diag/07_step.sql +36 -0
- pg_converter/bundled/packets/dba_idx_diag/08_step.sql +12 -0
- pg_converter/bundled/packets/dba_idx_diag/09_step.sql +18 -0
- pg_converter/bundled/packets/dba_idx_diag/10_step.sql +20 -0
- pg_converter/bundled/packets/dba_idx_diag/meta_data.json +5 -0
- pg_converter/bundled/packets/dba_lag/01_step.sql +8 -0
- pg_converter/bundled/packets/dba_lag/meta_data.json +11 -0
- pg_converter/bundled/packets/dba_locks/01_step.sql +23 -0
- pg_converter/bundled/packets/dba_locks/meta_data.json +11 -0
- pg_converter/bundled/packets/dba_long_queries/01_step.sql +22 -0
- pg_converter/bundled/packets/dba_long_queries/meta_data.json +11 -0
- pg_converter/bundled/packets/dba_top_tables/01_step.sql +56 -0
- pg_converter/bundled/packets/dba_top_tables/meta_data.json +11 -0
- pg_converter/bundled/packets/dba_xact_age/01_step.sql +10 -0
- pg_converter/bundled/packets/dba_xact_age/meta_data.json +11 -0
- pg_converter/bundled/packets/test_blocker_tx/01_step.sql +6 -0
- pg_converter/bundled/packets/test_blocker_tx/02_step.sql +5 -0
- pg_converter/bundled/packets/test_blocker_tx/meta_data.json +12 -0
- pg_converter/bundled/packets/test_common/01_step.sql +8 -0
- pg_converter/bundled/packets/test_common/02_gen_obj.sql +32 -0
- pg_converter/bundled/packets/test_common/02_step.sql +3 -0
- pg_converter/bundled/packets/test_common/03_gen_nsp.sql +7 -0
- pg_converter/bundled/packets/test_common/03_step.sql +3 -0
- pg_converter/bundled/packets/test_common/04_gen_nsp.sql +7 -0
- pg_converter/bundled/packets/test_common/04_gen_obj.sql +8 -0
- pg_converter/bundled/packets/test_common/04_step.sql +3 -0
- pg_converter/bundled/packets/test_common/05_step.sql +8 -0
- pg_converter/bundled/packets/test_common/06_step.sql +8 -0
- pg_converter/bundled/packets/test_common/meta_data.json +6 -0
- pg_converter/bundled/packets/test_dba_clone_schema/01_step.sql +47 -0
- pg_converter/bundled/packets/test_dba_clone_schema/02_step.sql +11 -0
- pg_converter/bundled/packets/test_dba_clone_schema/03_step.sql +11 -0
- pg_converter/bundled/packets/test_dba_clone_schema/meta_data.json +5 -0
- pg_converter/bundled/packets/test_dba_idx_diag/00_prepare.sql +128 -0
- pg_converter/bundled/packets/test_dba_idx_diag/01_step.sql_out +41 -0
- pg_converter/bundled/packets/test_dba_idx_diag/02_step.sql_out +15 -0
- pg_converter/bundled/packets/test_dba_idx_diag/03_step.sql_out +17 -0
- pg_converter/bundled/packets/test_dba_idx_diag/04_step.sql_out +58 -0
- pg_converter/bundled/packets/test_dba_idx_diag/05_step.sql_out +99 -0
- pg_converter/bundled/packets/test_dba_idx_diag/06_step.sql_out +31 -0
- pg_converter/bundled/packets/test_dba_idx_diag/07_step.sql_out +24 -0
- pg_converter/bundled/packets/test_dba_idx_diag/08_step.sql_out +8 -0
- pg_converter/bundled/packets/test_dba_idx_diag/09_step.sql_out +16 -0
- pg_converter/bundled/packets/test_dba_idx_diag/10_step.sql_out +16 -0
- pg_converter/bundled/packets/test_export_data/01_test.sql +22 -0
- pg_converter/bundled/packets/test_export_data/02_test.sql +2 -0
- pg_converter/bundled/packets/test_export_data/meta_data.json +9 -0
- pg_converter/bundled/packets/test_gens/00_step.sql +21 -0
- pg_converter/bundled/packets/test_gens/01_gen_nsp.sql +7 -0
- pg_converter/bundled/packets/test_gens/01_gen_obj.sql +8 -0
- pg_converter/bundled/packets/test_gens/01_step.sql +3 -0
- pg_converter/bundled/packets/test_gens/02_gen_nsp.sql +7 -0
- pg_converter/bundled/packets/test_gens/02_step.sql +3 -0
- pg_converter/bundled/packets/test_gens/03_gen_obj.sql +7 -0
- pg_converter/bundled/packets/test_gens/03_step.sql +3 -0
- pg_converter/bundled/packets/test_gens/meta_data.json +7 -0
- pg_converter/bundled/packets/test_get_version/01_step.sql +1 -0
- pg_converter/bundled/packets/test_get_version/meta_data.json +11 -0
- pg_converter/bundled/packets/test_insert_delete_select/01_step.sql +5 -0
- pg_converter/bundled/packets/test_insert_delete_select/meta_data.json +12 -0
- pg_converter/bundled/packets/test_insert_info/01_step.sql +11 -0
- pg_converter/bundled/packets/test_insert_info/meta_data.json +11 -0
- pg_converter/bundled/packets/test_int4_to_int8/01_step.sql +33 -0
- pg_converter/bundled/packets/test_int4_to_int8/02_gen_obj.sql +28 -0
- pg_converter/bundled/packets/test_int4_to_int8/02_step.sql +7 -0
- pg_converter/bundled/packets/test_int4_to_int8/03_step.sql +5 -0
- pg_converter/bundled/packets/test_int4_to_int8/04_step.sql +14 -0
- pg_converter/bundled/packets/test_int4_to_int8/05_step.sql +2 -0
- pg_converter/bundled/packets/test_int4_to_int8/06_step.sql +16 -0
- pg_converter/bundled/packets/test_int4_to_int8/07_step.sql +2 -0
- pg_converter/bundled/packets/test_int4_to_int8/meta_data.json +5 -0
- pg_converter/bundled/packets/test_int4_to_int8/run_once.sql +12 -0
- pg_converter/bundled/packets/test_maint/01_step.sql +1 -0
- pg_converter/bundled/packets/test_maint/02_step.sql +1 -0
- pg_converter/bundled/packets/test_maint/03_step.sql +5 -0
- pg_converter/bundled/packets/test_maint/04_step.sql +1 -0
- pg_converter/bundled/packets/test_maint/05_step.sql +3 -0
- pg_converter/bundled/packets/test_maint/06_step.sql +1 -0
- pg_converter/bundled/packets/test_maint/meta_data.json +12 -0
- pg_converter/bundled/packets/test_override_conf_param/01_step.sql +1 -0
- pg_converter/bundled/packets/test_override_conf_param/meta_data.json +4 -0
- pg_converter/bundled/packets/test_placeholders/00_step.sql +3 -0
- pg_converter/bundled/packets/test_placeholders/01_gen_obj.sql +3 -0
- pg_converter/bundled/packets/test_placeholders/01_step.sql +3 -0
- pg_converter/bundled/packets/test_placeholders/meta_data.json +3 -0
- pg_converter/bundled/packets/test_prepare_dbs/01_gen_obj.sql +25 -0
- pg_converter/bundled/packets/test_prepare_dbs/01_step.sql +1 -0
- pg_converter/bundled/packets/test_prepare_dbs/meta_data.json +4 -0
- pg_converter/bundled/packets/test_py_step/01_step.sql +9 -0
- pg_converter/bundled/packets/test_py_step/02_step.py +15 -0
- pg_converter/bundled/packets/test_py_step/data/data_a.txt +1 -0
- pg_converter/bundled/packets/test_py_step/data/data_b.txt +1 -0
- pg_converter/bundled/packets/test_py_step/meta_data.json +5 -0
- pg_converter/bundled/packets/test_raise/01_step.sql +8 -0
- pg_converter/bundled/packets/test_raise/meta_data.json +10 -0
- pg_converter/bundled/packets/test_read_only/01_step.sql +12 -0
- pg_converter/bundled/packets/test_read_only/meta_data.json +12 -0
- pg_converter/bundled/packets/test_read_only_exception/01_step.sql +12 -0
- pg_converter/bundled/packets/test_read_only_exception/meta_data.json +4 -0
- pg_converter/bundled/packets/test_read_only_gens/01_gen_nsp.sql +7 -0
- pg_converter/bundled/packets/test_read_only_gens/01_gen_obj.sql +8 -0
- pg_converter/bundled/packets/test_read_only_gens/01_step.sql +3 -0
- pg_converter/bundled/packets/test_read_only_gens/02_gen_nsp.sql +7 -0
- pg_converter/bundled/packets/test_read_only_gens/02_step.sql +3 -0
- pg_converter/bundled/packets/test_read_only_gens/03_gen_obj.sql +7 -0
- pg_converter/bundled/packets/test_read_only_gens/03_step.sql +3 -0
- pg_converter/bundled/packets/test_read_only_gens/meta_data.json +7 -0
- pg_converter/bundled/packets/test_read_only_raise/01_step.sql +8 -0
- pg_converter/bundled/packets/test_read_only_raise/meta_data.json +10 -0
- pg_converter/bundled/packets/test_skip_action_cancel/01_step.sql +5 -0
- pg_converter/bundled/packets/test_skip_action_cancel/02_gen_obj.sql +8 -0
- pg_converter/bundled/packets/test_skip_action_cancel/02_step.sql +3 -0
- pg_converter/bundled/packets/test_skip_action_cancel/03_step.sql +5 -0
- pg_converter/bundled/packets/test_skip_action_cancel/meta_data.json +4 -0
- pg_converter/bundled/packets/test_skip_step_cancel/01_step.sql +5 -0
- pg_converter/bundled/packets/test_skip_step_cancel/02_step.sql +5 -0
- pg_converter/bundled/packets/test_skip_step_cancel/03_step.sql +5 -0
- pg_converter/bundled/packets/test_skip_step_cancel/meta_data.json +4 -0
- pg_converter/bundled/packets/test_sleep/01_step.sql +5 -0
- pg_converter/bundled/packets/test_sleep/meta_data.json +5 -0
- pg_converter/bundled/packets/test_sleep_sigint/01_step.sql +5 -0
- pg_converter/bundled/packets/test_sleep_sigint/02_step.sql +5 -0
- pg_converter/bundled/packets/test_sleep_sigint/meta_data.json +5 -0
- pg_converter/bundled/packets/test_syntax_exception/01_step.sql +5 -0
- pg_converter/bundled/packets/test_template/02_gen_obj.sql +32 -0
- pg_converter/bundled/packets/test_template/02_step.sql +3 -0
- pg_converter/bundled/packets/test_template/03_gen_nsp.sql +7 -0
- pg_converter/bundled/packets/test_template/03_step.sql +3 -0
- pg_converter/bundled/packets/test_unique_exception/01_step.sql +6 -0
- pg_converter/bundled/packets/test_unique_exception/meta_data.json +11 -0
- pg_converter/bundled/packets/test_wait_tx/01_step.sql +6 -0
- pg_converter/bundled/packets/test_wait_tx/02_step.sql +1 -0
- pg_converter/bundled/packets/test_wait_tx/03_step.sql +1 -0
- pg_converter/bundled/packets/test_wait_tx/meta_data.json +12 -0
- pg_converter/cli.py +173 -0
- pg_converter/config.py +388 -0
- pg_converter/core/__init__.py +103 -0
- pg_converter/core/constants.py +114 -0
- pg_converter/core/context.py +212 -0
- pg_converter/core/database.py +433 -0
- pg_converter/core/events.py +70 -0
- pg_converter/core/executor.py +813 -0
- pg_converter/core/failures.py +269 -0
- pg_converter/core/lock_observer.py +396 -0
- pg_converter/core/packet.py +440 -0
- pg_converter/core/python_executor.py +256 -0
- pg_converter/core/session.py +248 -0
- pg_converter/core/tracker.py +597 -0
- pg_converter/logging_config.py +513 -0
- pg_converter/resources.py +26 -0
- pg_converter/runner.py +1587 -0
- pg_converter/utils/__init__.py +50 -0
- pg_converter/utils/common.py +171 -0
- pg_converter/utils/export.py +217 -0
- pg_converter/utils/signals.py +173 -0
- pg_converter/utils/webhooks.py +448 -0
- pg_converter-2.0b0.dist-info/METADATA +390 -0
- pg_converter-2.0b0.dist-info/RECORD +200 -0
- pg_converter-2.0b0.dist-info/WHEEL +5 -0
- pg_converter-2.0b0.dist-info/entry_points.txt +2 -0
- pg_converter-2.0b0.dist-info/licenses/LICENSE +21 -0
- pg_converter-2.0b0.dist-info/top_level.txt +1 -0
pg_converter/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PG Converter - PostgreSQL Migration Tool.
|
|
3
|
+
|
|
4
|
+
An async migration deployment tool for PostgreSQL databases.
|
|
5
|
+
Supports SQL and Python steps, generators, and action tracking.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pg_converter.core.constants import VERSION
|
|
9
|
+
|
|
10
|
+
__version__ = VERSION
|
|
11
|
+
__author__ = "O2eg"
|
|
12
|
+
|
|
13
|
+
from pg_converter.runner import PGConverter, main, run
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"PGConverter",
|
|
17
|
+
"main",
|
|
18
|
+
"run",
|
|
19
|
+
"VERSION",
|
|
20
|
+
"__version__",
|
|
21
|
+
]
|
pg_converter/__main__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# __main__.py
|
|
3
|
+
"""
|
|
4
|
+
PG Converter entry point.
|
|
5
|
+
|
|
6
|
+
Allows running as:
|
|
7
|
+
python -m pg_converter
|
|
8
|
+
python -m pg_converter --packet-name mypacket --db-name mydb
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from pg_converter.runner import run
|
|
12
|
+
|
|
13
|
+
if __name__ == "__main__":
|
|
14
|
+
run()
|
pg_converter/api.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Programmatic async API shared by the CLI and a future UI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
from argparse import Namespace
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, Mapping, Optional
|
|
11
|
+
|
|
12
|
+
from pg_converter.core.context import PGCResult
|
|
13
|
+
from pg_converter.core.events import ConfirmationHandler, EventHandler
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class RunRequest:
|
|
18
|
+
"""UI-neutral request for one packet operation."""
|
|
19
|
+
|
|
20
|
+
packet_name: str
|
|
21
|
+
db_name: str = "ALL"
|
|
22
|
+
operation: str = "run"
|
|
23
|
+
force: bool = False
|
|
24
|
+
skip_step_cancel: bool = False
|
|
25
|
+
skip_action_cancel: bool = False
|
|
26
|
+
sequential: bool = False
|
|
27
|
+
template: Optional[str] = None
|
|
28
|
+
placeholders: Mapping[str, Any] = field(default_factory=dict)
|
|
29
|
+
config_overrides: Mapping[str, Any] = field(default_factory=dict)
|
|
30
|
+
|
|
31
|
+
def to_namespace(self, config_file: Optional[Path] = None) -> Namespace:
|
|
32
|
+
operations = {"run", "status", "wipe", "stop", "unlock", "list"}
|
|
33
|
+
if self.operation not in operations:
|
|
34
|
+
raise ValueError(
|
|
35
|
+
"Unsupported operation "
|
|
36
|
+
f"{self.operation!r}; expected one of: " + ", ".join(sorted(operations))
|
|
37
|
+
)
|
|
38
|
+
return Namespace(
|
|
39
|
+
packet_name=self.packet_name,
|
|
40
|
+
db_name=self.db_name,
|
|
41
|
+
status=self.operation == "status",
|
|
42
|
+
wipe=self.operation == "wipe",
|
|
43
|
+
stop=self.operation == "stop",
|
|
44
|
+
unlock=self.operation == "unlock",
|
|
45
|
+
list=self.operation == "list",
|
|
46
|
+
force=self.force,
|
|
47
|
+
skip_step_cancel=self.skip_step_cancel,
|
|
48
|
+
skip_action_cancel=self.skip_action_cancel,
|
|
49
|
+
seq=self.sequential,
|
|
50
|
+
template=self.template,
|
|
51
|
+
placeholders=json.dumps(dict(self.placeholders)),
|
|
52
|
+
conf=json.dumps(dict(self.config_overrides)),
|
|
53
|
+
config_file=str(config_file) if config_file else None,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class PGConverterService:
|
|
58
|
+
"""Reusable service facade with no dependency on argparse or stdout."""
|
|
59
|
+
|
|
60
|
+
def __init__(self, config_file: Optional[Path] = None):
|
|
61
|
+
self.config_file = config_file
|
|
62
|
+
|
|
63
|
+
def start(
|
|
64
|
+
self,
|
|
65
|
+
request: RunRequest,
|
|
66
|
+
event_handler: Optional[EventHandler] = None,
|
|
67
|
+
confirmation_handler: Optional[ConfirmationHandler] = None,
|
|
68
|
+
) -> "RunHandle":
|
|
69
|
+
"""Start a cancellable operation without exposing argparse."""
|
|
70
|
+
# Local import avoids making the packet runner depend on this facade.
|
|
71
|
+
from pg_converter.runner import PGConverter
|
|
72
|
+
|
|
73
|
+
converter = PGConverter(
|
|
74
|
+
request.to_namespace(self.config_file),
|
|
75
|
+
event_handler=event_handler,
|
|
76
|
+
confirmation_handler=confirmation_handler,
|
|
77
|
+
)
|
|
78
|
+
return RunHandle(converter)
|
|
79
|
+
|
|
80
|
+
async def execute(
|
|
81
|
+
self,
|
|
82
|
+
request: RunRequest,
|
|
83
|
+
event_handler: Optional[EventHandler] = None,
|
|
84
|
+
confirmation_handler: Optional[ConfirmationHandler] = None,
|
|
85
|
+
) -> PGCResult:
|
|
86
|
+
handle = self.start(
|
|
87
|
+
request,
|
|
88
|
+
event_handler=event_handler,
|
|
89
|
+
confirmation_handler=confirmation_handler,
|
|
90
|
+
)
|
|
91
|
+
return await handle.wait()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class RunHandle:
|
|
95
|
+
"""UI-neutral handle for progress consumers and graceful cancellation."""
|
|
96
|
+
|
|
97
|
+
def __init__(self, converter: Any):
|
|
98
|
+
self._converter = converter
|
|
99
|
+
self._task: asyncio.Task[PGCResult] = asyncio.create_task(converter.run())
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def done(self) -> bool:
|
|
103
|
+
return self._task.done()
|
|
104
|
+
|
|
105
|
+
async def wait(self) -> PGCResult:
|
|
106
|
+
"""Wait for the complete terminal result."""
|
|
107
|
+
return await self._task
|
|
108
|
+
|
|
109
|
+
async def cancel(self) -> Dict[str, Dict[int, bool]]:
|
|
110
|
+
"""Cancel active PostgreSQL statements and let the run finalize."""
|
|
111
|
+
return await self._converter.cancel()
|
|
112
|
+
|
|
113
|
+
def __await__(self):
|
|
114
|
+
return self.wait().__await__()
|
|
File without changes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[databases]
|
|
2
|
+
# Plain connection (pq:// and postgresql:// are both supported).
|
|
3
|
+
development = postgresql://user:password@localhost:5432/development
|
|
4
|
+
|
|
5
|
+
# TLS with server certificate and hostname verification.
|
|
6
|
+
production = postgresql://user:password@db.example.com:5432/production?sslmode=verify-full&sslrootcert=/etc/pg_converter/ca.crt
|
|
7
|
+
|
|
8
|
+
# Mutual TLS with a client certificate. Percent-encode special URL characters
|
|
9
|
+
# in credentials and paths when required.
|
|
10
|
+
production_mtls = postgresql://user@db.example.com:5432/production?sslmode=verify-full&sslrootcert=/etc/pg_converter/ca.crt&sslcert=/etc/pg_converter/client.crt&sslkey=/etc/pg_converter/client.key
|
|
11
|
+
|
|
12
|
+
# The equivalent libpq key=value form is also supported:
|
|
13
|
+
# reporting = host=db.example.com port=5432 dbname=reporting user=pgc sslmode=verify-ca sslrootcert=/etc/pg_converter/ca.crt sslcert=/etc/pg_converter/client.crt sslkey=/etc/pg_converter/client.key
|
|
14
|
+
|
|
15
|
+
[main]
|
|
16
|
+
application_name = pg_converter
|
|
17
|
+
lock_observer_sleep_interval = 5
|
|
18
|
+
execute_sql = true
|
|
19
|
+
conn_exception_sleep_interval = 5
|
|
20
|
+
# Bounded reconnect/action retry policy. The sleep value above is the
|
|
21
|
+
# initial backoff delay.
|
|
22
|
+
connect_retry_attempts = 5
|
|
23
|
+
action_retry_attempts = 3
|
|
24
|
+
retry_max_delay = 30
|
|
25
|
+
retry_multiplier = 2
|
|
26
|
+
retry_jitter = 0.2
|
|
27
|
+
connect_timeout = 30
|
|
28
|
+
cancel_wait_tx_timeout = 3 minutes
|
|
29
|
+
cancel_blocker_tx_timeout = 60 seconds
|
|
30
|
+
detailed_traceback = false
|
|
31
|
+
db_name_all_confirmation = true
|
|
32
|
+
schema_location = pgc
|
|
33
|
+
|
|
34
|
+
[log]
|
|
35
|
+
log_level = INFO
|
|
36
|
+
log_sql = true
|
|
37
|
+
file_maxmbytes = 50
|
|
38
|
+
file_backupcount = 5
|
|
39
|
+
|
|
40
|
+
[mattermost_hooks]
|
|
41
|
+
url = https://mattermost.example.com
|
|
42
|
+
chat_keys = pgc-alert/replace-with-hook-key,pgc-dba/replace-with-hook-key
|
|
43
|
+
|
|
44
|
+
[slack_hooks]
|
|
45
|
+
url = https://hooks.slack.com/services
|
|
46
|
+
chat_keys = pgc-alert=replace/with/hook-key
|
|
47
|
+
|
|
48
|
+
[postgresql]
|
|
49
|
+
deadlock_timeout = 100ms
|
|
50
|
+
statement_timeout = 1h
|
|
51
|
+
vacuum_cost_limit = 3000
|
|
52
|
+
work_mem = 200MB
|
|
53
|
+
maintenance_work_mem = 1GB
|
|
54
|
+
timezone = UTC
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
WITH btree_index_atts AS (
|
|
2
|
+
SELECT nspname,
|
|
3
|
+
ci.relname as index_name,
|
|
4
|
+
ci.reltuples,
|
|
5
|
+
ci.relpages,
|
|
6
|
+
i.indrelid, i.indexrelid,
|
|
7
|
+
ci.relam,
|
|
8
|
+
ct.relname as tablename,
|
|
9
|
+
regexp_split_to_table(indkey::text, ' ')::smallint AS attnum,
|
|
10
|
+
coalesce(substring(array_to_string(ci.reloptions, ' ') from '%fillfactor=#"__#"%' for '#')::int2, 100) as fillfactor,
|
|
11
|
+
indexrelid as index_oid,
|
|
12
|
+
(SELECT pg_get_indexdef (i.indexrelid)) as def,
|
|
13
|
+
indisunique::integer,
|
|
14
|
+
indisprimary::integer,
|
|
15
|
+
(select pg_get_constraintdef(cn.oid)) as constraintdef, cn.oid as conoid,
|
|
16
|
+
conname,
|
|
17
|
+
cn.contype,
|
|
18
|
+
cn.conrelid
|
|
19
|
+
FROM pg_index i
|
|
20
|
+
JOIN pg_class AS ci ON i.indexrelid = ci.oid and ci.relkind = 'i'
|
|
21
|
+
JOIN pg_class AS ct ON i.indrelid = ct.oid and ct.relkind in ('r', 'm', 't')
|
|
22
|
+
-- where relkind: r = ordinary table, i = index, S = sequence, v = view, m = materialized view,
|
|
23
|
+
-- c = composite type, t = TOAST table, f = foreign table
|
|
24
|
+
JOIN pg_namespace n ON n.oid = ci.relnamespace
|
|
25
|
+
JOIN pg_am a ON ci.relam = a.oid
|
|
26
|
+
LEFT JOIN pg_constraint cn ON cn.connamespace = n.oid and cn.conindid = i.indexrelid
|
|
27
|
+
WHERE a.amname = 'btree' and ci.relpages > 3000
|
|
28
|
+
AND nspname NOT IN ('pg_catalog', 'information_schema')
|
|
29
|
+
AND ct.reltuples > 5000
|
|
30
|
+
AND ci.relname not in ('d_last_session_site_id_ip_user_agent')
|
|
31
|
+
),
|
|
32
|
+
index_item_sizes AS (
|
|
33
|
+
SELECT
|
|
34
|
+
ind_atts.nspname, ind_atts.index_name,
|
|
35
|
+
ind_atts.reltuples, ind_atts.relpages, ind_atts.relam,
|
|
36
|
+
indrelid AS table_oid, index_oid,
|
|
37
|
+
current_setting('block_size')::numeric AS bs, fillfactor,
|
|
38
|
+
8 AS maxalign,
|
|
39
|
+
24 AS pagehdr,
|
|
40
|
+
CASE WHEN max(coalesce(pg_stats.null_frac, 0)) = 0
|
|
41
|
+
THEN 2
|
|
42
|
+
ELSE 6
|
|
43
|
+
END AS index_tuple_hdr,
|
|
44
|
+
sum((1-coalesce(pg_stats.null_frac, 0)) * coalesce(pg_stats.avg_width, 1024)) AS nulldatawidth,
|
|
45
|
+
max(def) as def, max(indisunique) as indisunique, max(indisprimary) as indisprimary,
|
|
46
|
+
max(constraintdef) as constraintdef, max(conoid) as conoid, max(conname) as conname,
|
|
47
|
+
max(contype) as contype, max(conrelid) as conrelid -- ext info
|
|
48
|
+
FROM pg_attribute
|
|
49
|
+
JOIN btree_index_atts AS ind_atts ON pg_attribute.attrelid = ind_atts.indexrelid AND pg_attribute.attnum = ind_atts.attnum
|
|
50
|
+
JOIN pg_stats ON pg_stats.schemaname = ind_atts.nspname
|
|
51
|
+
-- stats for regular index columns
|
|
52
|
+
AND (
|
|
53
|
+
(
|
|
54
|
+
pg_stats.tablename = ind_atts.tablename AND
|
|
55
|
+
pg_stats.attname = pg_catalog.pg_get_indexdef(pg_attribute.attrelid, pg_attribute.attnum, TRUE)
|
|
56
|
+
)
|
|
57
|
+
-- stats for functional indexes
|
|
58
|
+
OR (pg_stats.tablename = ind_atts.index_name AND pg_stats.attname = pg_attribute.attname))
|
|
59
|
+
WHERE pg_attribute.attnum > 0
|
|
60
|
+
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
|
61
|
+
),
|
|
62
|
+
index_aligned_est AS (
|
|
63
|
+
SELECT maxalign, bs, nspname, index_name, reltuples,
|
|
64
|
+
relpages, relam, table_oid, index_oid,
|
|
65
|
+
coalesce (
|
|
66
|
+
ceil (
|
|
67
|
+
reltuples * ( 6
|
|
68
|
+
+ maxalign
|
|
69
|
+
- CASE
|
|
70
|
+
WHEN index_tuple_hdr%maxalign = 0 THEN maxalign
|
|
71
|
+
ELSE index_tuple_hdr%maxalign
|
|
72
|
+
END
|
|
73
|
+
+ nulldatawidth
|
|
74
|
+
+ maxalign
|
|
75
|
+
- CASE /* Add padding to the data to align on MAXALIGN */
|
|
76
|
+
WHEN nulldatawidth::integer%maxalign = 0 THEN maxalign
|
|
77
|
+
ELSE nulldatawidth::integer%maxalign
|
|
78
|
+
END
|
|
79
|
+
)::numeric
|
|
80
|
+
/ ( bs - pagehdr::NUMERIC )
|
|
81
|
+
+1 )
|
|
82
|
+
, 0 )
|
|
83
|
+
as expected,
|
|
84
|
+
def, indisunique, indisprimary, constraintdef, conoid, conname, contype, conrelid, fillfactor
|
|
85
|
+
FROM index_item_sizes
|
|
86
|
+
),
|
|
87
|
+
raw_bloat AS (
|
|
88
|
+
SELECT current_database() as dbname, nspname, pg_class.relname AS table_name, index_name,
|
|
89
|
+
bs*(index_aligned_est.relpages)::bigint AS totalbytes, expected,
|
|
90
|
+
CASE
|
|
91
|
+
WHEN index_aligned_est.relpages <= expected
|
|
92
|
+
THEN 0
|
|
93
|
+
ELSE bs*(index_aligned_est.relpages-expected)::bigint
|
|
94
|
+
END AS wastedbytes,
|
|
95
|
+
CASE
|
|
96
|
+
WHEN index_aligned_est.relpages <= expected
|
|
97
|
+
THEN 0
|
|
98
|
+
ELSE bs*(index_aligned_est.relpages-expected)::bigint * 100 / (bs*(index_aligned_est.relpages)::bigint)
|
|
99
|
+
END AS realbloat,
|
|
100
|
+
pg_relation_size(index_aligned_est.table_oid) as table_bytes,
|
|
101
|
+
stat.idx_scan as index_scans,
|
|
102
|
+
def, indisunique, indisprimary, constraintdef, conoid, conname, contype, conrelid, fillfactor -- ext info
|
|
103
|
+
FROM index_aligned_est
|
|
104
|
+
JOIN pg_class ON pg_class.oid=index_aligned_est.table_oid
|
|
105
|
+
JOIN pg_stat_user_indexes AS stat ON index_aligned_est.index_oid = stat.indexrelid
|
|
106
|
+
)
|
|
107
|
+
SELECT
|
|
108
|
+
nspname as schema_name, table_name, index_name,
|
|
109
|
+
round(realbloat::numeric, 2) as bloat_pct,
|
|
110
|
+
pg_size_pretty(wastedbytes::NUMERIC) as bloat_size,
|
|
111
|
+
pg_size_pretty(totalbytes::NUMERIC) as index_size,
|
|
112
|
+
pg_size_pretty(table_bytes::NUMERIC) as table_size,
|
|
113
|
+
fillfactor
|
|
114
|
+
FROM raw_bloat
|
|
115
|
+
WHERE (round(realbloat::numeric, 2) >= 80 and wastedbytes/(1024^2)::NUMERIC > 5)
|
|
116
|
+
ORDER BY wastedbytes DESC nulls last
|
|
117
|
+
LIMIT 10;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
with step1 as (
|
|
2
|
+
select
|
|
3
|
+
tbl.oid tblid,
|
|
4
|
+
ns.nspname as schema_name,
|
|
5
|
+
tbl.relname as table_name,
|
|
6
|
+
tbl.reltuples,
|
|
7
|
+
tbl.relpages as heappages,
|
|
8
|
+
coalesce(toast.relpages, 0) as toastpages,
|
|
9
|
+
coalesce(toast.reltuples, 0) as toasttuples,
|
|
10
|
+
coalesce(substring(array_to_string(tbl.reloptions, ' ') from '%fillfactor=#"__#"%' for '#')::int2, 100) as fillfactor,
|
|
11
|
+
current_setting('block_size')::numeric as bs,
|
|
12
|
+
case when version() ~ 'mingw32|64-bit|x86_64|ppc64|ia64|amd64' then 8 else 4 end as ma, -- NS: TODO: check it
|
|
13
|
+
24 as page_hdr,
|
|
14
|
+
23 + case when max(coalesce(null_frac, 0)) > 0 then (7 + count(*)) / 8 else 0::int end as tpl_hdr_size,
|
|
15
|
+
sum((1 - coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 1024)) as tpl_data_size,
|
|
16
|
+
bool_or(att.atttypid = 'pg_catalog.name'::regtype) or count(att.attname) <> count(s.attname) as is_na
|
|
17
|
+
from pg_attribute as att
|
|
18
|
+
join pg_class as tbl on att.attrelid = tbl.oid and tbl.relkind = 'r'
|
|
19
|
+
join pg_namespace as ns on ns.oid = tbl.relnamespace
|
|
20
|
+
join pg_stats as s on s.schemaname = ns.nspname and s.tablename = tbl.relname and not s.inherited and s.attname = att.attname
|
|
21
|
+
left join pg_class as toast on tbl.reltoastrelid = toast.oid
|
|
22
|
+
where att.attnum > 0 and not att.attisdropped and s.schemaname not in ('pg_catalog', 'information_schema') and tbl.relpages > 2000
|
|
23
|
+
group by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
|
24
|
+
order by 2, 3
|
|
25
|
+
), step2 as (
|
|
26
|
+
select
|
|
27
|
+
*,
|
|
28
|
+
(
|
|
29
|
+
4 + tpl_hdr_size + tpl_data_size + (2 * ma)
|
|
30
|
+
- case when tpl_hdr_size % ma = 0 then ma else tpl_hdr_size % ma end
|
|
31
|
+
- case when ceil(tpl_data_size)::int % ma = 0 then ma else ceil(tpl_data_size)::int % ma end
|
|
32
|
+
) as tpl_size,
|
|
33
|
+
bs - page_hdr as size_per_block,
|
|
34
|
+
(heappages + toastpages) as tblpages
|
|
35
|
+
from step1
|
|
36
|
+
), step3 as (
|
|
37
|
+
select
|
|
38
|
+
*,
|
|
39
|
+
ceil(reltuples / ((bs - page_hdr) / tpl_size)) + ceil(toasttuples / 4) as est_tblpages,
|
|
40
|
+
ceil(reltuples / ((bs - page_hdr) * fillfactor / (tpl_size * 100))) + ceil(toasttuples / 4) as est_tblpages_ff
|
|
41
|
+
-- , stattuple.pgstattuple(tblid) as pst
|
|
42
|
+
from step2
|
|
43
|
+
), step4 as (
|
|
44
|
+
select
|
|
45
|
+
*,
|
|
46
|
+
tblpages * bs as real_size,
|
|
47
|
+
(tblpages - est_tblpages) * bs as extra_size,
|
|
48
|
+
case when tblpages - est_tblpages > 0 then 100 * (tblpages - est_tblpages) / tblpages::float else 0 end as extra_ratio,
|
|
49
|
+
(tblpages - est_tblpages_ff) * bs as bloat_size,
|
|
50
|
+
case when tblpages - est_tblpages_ff > 0 then 100 * (tblpages - est_tblpages_ff) / tblpages::float else 0 end as bloat_ratio
|
|
51
|
+
from step3
|
|
52
|
+
)
|
|
53
|
+
select
|
|
54
|
+
step4.schema_name,
|
|
55
|
+
step4.table_name,
|
|
56
|
+
round(bloat_ratio::numeric, 2) as bloat_pct,
|
|
57
|
+
pg_size_pretty(bloat_size::numeric)::text as bloat_size,
|
|
58
|
+
pg_size_pretty(real_size::numeric) as table_size,
|
|
59
|
+
case
|
|
60
|
+
when (real_size - bloat_size)::numeric >=0
|
|
61
|
+
then '~' || pg_size_pretty((real_size - bloat_size)::numeric)
|
|
62
|
+
else null
|
|
63
|
+
end as "live",
|
|
64
|
+
(
|
|
65
|
+
select
|
|
66
|
+
coalesce(substring(array_to_string(reloptions, ' ') from 'fillfactor=([0-9]+)')::smallint, 100)
|
|
67
|
+
from pg_class
|
|
68
|
+
where oid = tblid
|
|
69
|
+
) as "fillfactor"
|
|
70
|
+
from step4
|
|
71
|
+
where bloat_ratio >= 80 and bloat_size > 1024 * 1000 * 10
|
|
72
|
+
order by bloat_size desc nulls last
|
|
73
|
+
limit 10;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"responsible": "Oleg Gurov",
|
|
3
|
+
"description": "check index/heap bloat",
|
|
4
|
+
"type": "read_only",
|
|
5
|
+
"client_min_messages": "INFO",
|
|
6
|
+
"hook": {
|
|
7
|
+
"type": "mattermost",
|
|
8
|
+
"username": "pg_converter",
|
|
9
|
+
"verbosity": ["resultset"],
|
|
10
|
+
"channel": "pgc-alert",
|
|
11
|
+
"message": ":warning: @here Found extra bloat:"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
------------------
|
|
2
|
+
-- tx start
|
|
3
|
+
do $$
|
|
4
|
+
begin
|
|
5
|
+
if exists(select 1
|
|
6
|
+
from pg_class
|
|
7
|
+
where '{autovacuum_enabled=false}'::text[] @> reloptions and relkind = 'r')
|
|
8
|
+
then
|
|
9
|
+
raise exception 'autovacuum_enabled=false on some tables! %', (
|
|
10
|
+
select array_agg(quote_ident(n.nspname) || '.' || quote_ident(c.relname))::text
|
|
11
|
+
from pg_class c
|
|
12
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
13
|
+
where '{autovacuum_enabled=false}'::text[] @> reloptions and relkind = 'r'
|
|
14
|
+
);
|
|
15
|
+
end if;
|
|
16
|
+
end$$;
|
|
17
|
+
-- tx end
|
|
18
|
+
------------------
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "read_only",
|
|
3
|
+
"hook": {
|
|
4
|
+
"type": "mattermost",
|
|
5
|
+
"username": "pg_converter",
|
|
6
|
+
"channel": "pgc-alert",
|
|
7
|
+
"message": ":warning: @here Found tables with disabled autovacuum! Use `ALTER TABLE tbl SET (autovacuum_enabled = true);`",
|
|
8
|
+
"verbosity": "raise"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
select
|
|
2
|
+
'' as maint,
|
|
3
|
+
-- c.oid,
|
|
4
|
+
-- (select spcname from pg_tablespace where oid = reltablespace) as tblspace,
|
|
5
|
+
nspname as schema_name,
|
|
6
|
+
relname as table_name,
|
|
7
|
+
t.typname,
|
|
8
|
+
attname,
|
|
9
|
+
(select pg_get_serial_sequence(quote_ident(nspname) || '.' || quote_ident(relname), attname)) as seq
|
|
10
|
+
from pg_index i
|
|
11
|
+
join pg_class c on c.oid = i.indrelid
|
|
12
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
13
|
+
join pg_attribute a on
|
|
14
|
+
a.attrelid = i.indrelid
|
|
15
|
+
and a.attnum = any(i.indkey)
|
|
16
|
+
join pg_type t on t.oid = atttypid
|
|
17
|
+
where
|
|
18
|
+
i.indisprimary
|
|
19
|
+
-- 8192 * 10000 = 81,92MB
|
|
20
|
+
and c.relpages > 10000
|
|
21
|
+
and t.typname in ('int2', 'int4')
|
|
22
|
+
and nspname <> 'pg_toast'
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
do $$
|
|
2
|
+
declare
|
|
3
|
+
val bigint;
|
|
4
|
+
ratio numeric;
|
|
5
|
+
begin
|
|
6
|
+
-- schema_name - GEN_OBJ_FLD_1
|
|
7
|
+
-- table_name - GEN_OBJ_FLD_2
|
|
8
|
+
-- typname - GEN_OBJ_FLD_3
|
|
9
|
+
-- attname - GEN_OBJ_FLD_4
|
|
10
|
+
-- seq - GEN_OBJ_FLD_5
|
|
11
|
+
|
|
12
|
+
if 'GEN_OBJ_FLD_5' = 'None' then
|
|
13
|
+
execute format('select max(%I) from %I.%I;', 'GEN_OBJ_FLD_4', 'GEN_OBJ_FLD_1', 'GEN_OBJ_FLD_2') into val;
|
|
14
|
+
else
|
|
15
|
+
execute format('SELECT last_value FROM %s', 'GEN_OBJ_FLD_5') into val;
|
|
16
|
+
end if;
|
|
17
|
+
if 'GEN_OBJ_FLD_3' = 'int4' then
|
|
18
|
+
ratio := (val::numeric / 2^31)::numeric;
|
|
19
|
+
elsif 'GEN_OBJ_FLD_3' = 'int2' then
|
|
20
|
+
ratio := (val::numeric / 2^15)::numeric;
|
|
21
|
+
end if;
|
|
22
|
+
if ratio > 0.7 then -- report only if > 50% of capacity is reached
|
|
23
|
+
raise notice '%', format('tbl = %s pk = %s type = %s current_value = %s capacity = %s',
|
|
24
|
+
coalesce(nullif(quote_ident('GEN_OBJ_FLD_1'), 'public') || '.', '') || quote_ident('GEN_OBJ_FLD_2'),
|
|
25
|
+
'GEN_OBJ_FLD_4',
|
|
26
|
+
'GEN_OBJ_FLD_3',
|
|
27
|
+
val,
|
|
28
|
+
round(100 * ratio, 2)
|
|
29
|
+
);
|
|
30
|
+
end if;
|
|
31
|
+
end;
|
|
32
|
+
$$
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "read_only",
|
|
3
|
+
"client_min_messages": "NOTICE",
|
|
4
|
+
"hook": {
|
|
5
|
+
"type": "mattermost",
|
|
6
|
+
"username": "pg_converter",
|
|
7
|
+
"verbosity": ["raise"],
|
|
8
|
+
"channel": "pgc-alert",
|
|
9
|
+
"message": ":warning: @here Found tables with low int4 capacity:"
|
|
10
|
+
},
|
|
11
|
+
"postgresql": {
|
|
12
|
+
"statement_timeout": "180s"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
select n.nspname, c.relname as tbl, ci.relname as idx, indisvalid, indisready
|
|
2
|
+
from pg_index i
|
|
3
|
+
join pg_class c on i.indrelid = c.oid and c.relkind in ('r', 'm')
|
|
4
|
+
join pg_class ci on i.indexrelid = ci.oid and ci.relkind = 'i'
|
|
5
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
6
|
+
where indisvalid = false or indisready = false
|
|
7
|
+
order by c.relpages desc
|
|
8
|
+
limit 100
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"responsible": "Oleg Gurov",
|
|
3
|
+
"type": "read_only",
|
|
4
|
+
"client_min_messages": "INFO",
|
|
5
|
+
"hook": {
|
|
6
|
+
"type": "mattermost",
|
|
7
|
+
"username": "pg_converter",
|
|
8
|
+
"verbosity": ["resultset"],
|
|
9
|
+
"channel": "pgc-alert",
|
|
10
|
+
"message": ":warning: @here List of `invalid` and `not ready` indexes:"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
SELECT format('Low number of free connections: %s (%s) [max_connections = %s]', T.c, T.v || '%', T.s) as alert
|
|
2
|
+
FROM (
|
|
3
|
+
SELECT
|
|
4
|
+
round(((select count(1) from pg_stat_activity) * 100)::numeric / setting::integer, 2) as v,
|
|
5
|
+
setting::integer - (select count(1) from pg_stat_activity) as c,
|
|
6
|
+
setting as s
|
|
7
|
+
FROM pg_settings
|
|
8
|
+
WHERE name = 'max_connections'
|
|
9
|
+
) T WHERE T.v > 70
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"responsible": "Oleg Gurov",
|
|
3
|
+
"description": "Check available connections",
|
|
4
|
+
"type": "read_only",
|
|
5
|
+
"client_min_messages": "INFO",
|
|
6
|
+
"hook": {
|
|
7
|
+
"type": "mattermost",
|
|
8
|
+
"username": "pg_converter",
|
|
9
|
+
"verbosity": ["resultset"],
|
|
10
|
+
"channel": "pgc-alert",
|
|
11
|
+
"message": ":warning: @here Found problem with max_connections:"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
select n.nspname,
|
|
2
|
+
c.relname,
|
|
3
|
+
c.reltuples::bigint,
|
|
4
|
+
st.last_autoanalyze,
|
|
5
|
+
st.last_autovacuum,
|
|
6
|
+
st.last_analyze,
|
|
7
|
+
st.last_vacuum
|
|
8
|
+
from pg_class c
|
|
9
|
+
join pg_namespace n on c.relnamespace = n.oid
|
|
10
|
+
left join pg_stat_all_tables st on st.relid = c.oid
|
|
11
|
+
where c.reltuples > 1000 and c.relkind in ('r', 'm') and not(n.nspname = 'pg_catalog' and c.relname = 'pg_shdepend') and
|
|
12
|
+
(
|
|
13
|
+
(st.last_autoanalyze is null and st.last_autovacuum is null and st.last_analyze is null and st.last_vacuum is null) -- attention!
|
|
14
|
+
OR
|
|
15
|
+
(
|
|
16
|
+
n.nspname not in('pg_toast', 'pg_catalog')
|
|
17
|
+
AND
|
|
18
|
+
LEAST(
|
|
19
|
+
age(now(), coalesce(st.last_autoanalyze, '2000-01-01'::timestamp with time zone)),
|
|
20
|
+
age(now(), coalesce(st.last_analyze, '2000-01-01'::timestamp with time zone))
|
|
21
|
+
) > '15 days'
|
|
22
|
+
AND
|
|
23
|
+
LEAST(
|
|
24
|
+
age(now(), coalesce(st.last_autovacuum, '2000-01-01'::timestamp with time zone)),
|
|
25
|
+
age(now(), coalesce(st.last_vacuum, '2000-01-01'::timestamp with time zone))
|
|
26
|
+
) > '15 days'
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
order by
|
|
30
|
+
st.last_autoanalyze asc nulls first,
|
|
31
|
+
st.last_autovacuum asc nulls first,
|
|
32
|
+
st.last_analyze asc nulls first,
|
|
33
|
+
st.last_vacuum asc nulls first
|
|
34
|
+
limit 10;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"responsible": "Oleg Gurov",
|
|
3
|
+
"description": "check outdated statistics",
|
|
4
|
+
"type": "read_only",
|
|
5
|
+
"client_min_messages": "INFO",
|
|
6
|
+
"hook": {
|
|
7
|
+
"type": "mattermost",
|
|
8
|
+
"username": "pg_converter",
|
|
9
|
+
"verbosity": ["resultset"],
|
|
10
|
+
"channel": "pgc-alert",
|
|
11
|
+
"message": ":warning: @here Found outdated statistics:"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
SELECT
|
|
2
|
+
n.nspname,
|
|
3
|
+
c.relname,
|
|
4
|
+
age(c.relfrozenxid) as relfrozenxid_age,
|
|
5
|
+
mxid_age(c.relminmxid) as relminmxid_age
|
|
6
|
+
FROM pg_class c
|
|
7
|
+
JOIN pg_namespace n on c.relnamespace = n.oid
|
|
8
|
+
WHERE c.relkind IN ('r', 'm', 't') and
|
|
9
|
+
(age(c.relfrozenxid) > 1300000000 or mxid_age(c.relminmxid) > 1400000000)
|
|
10
|
+
ORDER BY age(c.relfrozenxid) desc
|
|
11
|
+
LIMIT 10;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"responsible": "Oleg Gurov",
|
|
3
|
+
"description": "Check xact age of relations and databases",
|
|
4
|
+
"type": "read_only",
|
|
5
|
+
"client_min_messages": "INFO",
|
|
6
|
+
"hook": {
|
|
7
|
+
"type": "mattermost",
|
|
8
|
+
"username": "pg_converter",
|
|
9
|
+
"verbosity": ["resultset"],
|
|
10
|
+
"channel": "pgc-alert",
|
|
11
|
+
"message": ":warning: @here Found problems with frozenxid:"
|
|
12
|
+
}
|
|
13
|
+
}
|