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.
Files changed (200) hide show
  1. pg_converter/__init__.py +21 -0
  2. pg_converter/__main__.py +14 -0
  3. pg_converter/api.py +114 -0
  4. pg_converter/bundled/__init__.py +0 -0
  5. pg_converter/bundled/conf/pg_converter.conf.example +54 -0
  6. pg_converter/bundled/packets/alert_bloat/01_step.sql +117 -0
  7. pg_converter/bundled/packets/alert_bloat/02_step.sql +73 -0
  8. pg_converter/bundled/packets/alert_bloat/meta_data.json +13 -0
  9. pg_converter/bundled/packets/alert_check_autovacuum/01_step.sql +18 -0
  10. pg_converter/bundled/packets/alert_check_autovacuum/meta_data.json +10 -0
  11. pg_converter/bundled/packets/alert_int4_capacity/01_gen_obj.sql +22 -0
  12. pg_converter/bundled/packets/alert_int4_capacity/01_step.sql +32 -0
  13. pg_converter/bundled/packets/alert_int4_capacity/meta_data.json +14 -0
  14. pg_converter/bundled/packets/alert_invalid_indexes/01_step.sql +8 -0
  15. pg_converter/bundled/packets/alert_invalid_indexes/meta_data.json +12 -0
  16. pg_converter/bundled/packets/alert_max_connections/01_step.sql +9 -0
  17. pg_converter/bundled/packets/alert_max_connections/meta_data.json +13 -0
  18. pg_converter/bundled/packets/alert_stat/01_step.sql +34 -0
  19. pg_converter/bundled/packets/alert_stat/meta_data.json +13 -0
  20. pg_converter/bundled/packets/alert_xact_age/01_step.sql +11 -0
  21. pg_converter/bundled/packets/alert_xact_age/02_step.sql +7 -0
  22. pg_converter/bundled/packets/alert_xact_age/meta_data.json +13 -0
  23. pg_converter/bundled/packets/dba_active_maint/01_step.sql +34 -0
  24. pg_converter/bundled/packets/dba_active_maint/meta_data.json +11 -0
  25. pg_converter/bundled/packets/dba_clone_schema/01_step.sql +433 -0
  26. pg_converter/bundled/packets/dba_clone_schema/meta_data.json +4 -0
  27. pg_converter/bundled/packets/dba_find_db/01_step.sql +1 -0
  28. pg_converter/bundled/packets/dba_find_db/meta_data.json +5 -0
  29. pg_converter/bundled/packets/dba_get_conf/01_step.sql +32 -0
  30. pg_converter/bundled/packets/dba_get_conf/meta_data.json +5 -0
  31. pg_converter/bundled/packets/dba_get_version/01_step.sql +1 -0
  32. pg_converter/bundled/packets/dba_get_version/meta_data.json +11 -0
  33. pg_converter/bundled/packets/dba_idx_diag/01_step.sql +21 -0
  34. pg_converter/bundled/packets/dba_idx_diag/02_step.sql +16 -0
  35. pg_converter/bundled/packets/dba_idx_diag/03_step.sql +16 -0
  36. pg_converter/bundled/packets/dba_idx_diag/04_step.sql +23 -0
  37. pg_converter/bundled/packets/dba_idx_diag/05_step.sql +29 -0
  38. pg_converter/bundled/packets/dba_idx_diag/06_step.sql +46 -0
  39. pg_converter/bundled/packets/dba_idx_diag/07_step.sql +36 -0
  40. pg_converter/bundled/packets/dba_idx_diag/08_step.sql +12 -0
  41. pg_converter/bundled/packets/dba_idx_diag/09_step.sql +18 -0
  42. pg_converter/bundled/packets/dba_idx_diag/10_step.sql +20 -0
  43. pg_converter/bundled/packets/dba_idx_diag/meta_data.json +5 -0
  44. pg_converter/bundled/packets/dba_lag/01_step.sql +8 -0
  45. pg_converter/bundled/packets/dba_lag/meta_data.json +11 -0
  46. pg_converter/bundled/packets/dba_locks/01_step.sql +23 -0
  47. pg_converter/bundled/packets/dba_locks/meta_data.json +11 -0
  48. pg_converter/bundled/packets/dba_long_queries/01_step.sql +22 -0
  49. pg_converter/bundled/packets/dba_long_queries/meta_data.json +11 -0
  50. pg_converter/bundled/packets/dba_top_tables/01_step.sql +56 -0
  51. pg_converter/bundled/packets/dba_top_tables/meta_data.json +11 -0
  52. pg_converter/bundled/packets/dba_xact_age/01_step.sql +10 -0
  53. pg_converter/bundled/packets/dba_xact_age/meta_data.json +11 -0
  54. pg_converter/bundled/packets/test_blocker_tx/01_step.sql +6 -0
  55. pg_converter/bundled/packets/test_blocker_tx/02_step.sql +5 -0
  56. pg_converter/bundled/packets/test_blocker_tx/meta_data.json +12 -0
  57. pg_converter/bundled/packets/test_common/01_step.sql +8 -0
  58. pg_converter/bundled/packets/test_common/02_gen_obj.sql +32 -0
  59. pg_converter/bundled/packets/test_common/02_step.sql +3 -0
  60. pg_converter/bundled/packets/test_common/03_gen_nsp.sql +7 -0
  61. pg_converter/bundled/packets/test_common/03_step.sql +3 -0
  62. pg_converter/bundled/packets/test_common/04_gen_nsp.sql +7 -0
  63. pg_converter/bundled/packets/test_common/04_gen_obj.sql +8 -0
  64. pg_converter/bundled/packets/test_common/04_step.sql +3 -0
  65. pg_converter/bundled/packets/test_common/05_step.sql +8 -0
  66. pg_converter/bundled/packets/test_common/06_step.sql +8 -0
  67. pg_converter/bundled/packets/test_common/meta_data.json +6 -0
  68. pg_converter/bundled/packets/test_dba_clone_schema/01_step.sql +47 -0
  69. pg_converter/bundled/packets/test_dba_clone_schema/02_step.sql +11 -0
  70. pg_converter/bundled/packets/test_dba_clone_schema/03_step.sql +11 -0
  71. pg_converter/bundled/packets/test_dba_clone_schema/meta_data.json +5 -0
  72. pg_converter/bundled/packets/test_dba_idx_diag/00_prepare.sql +128 -0
  73. pg_converter/bundled/packets/test_dba_idx_diag/01_step.sql_out +41 -0
  74. pg_converter/bundled/packets/test_dba_idx_diag/02_step.sql_out +15 -0
  75. pg_converter/bundled/packets/test_dba_idx_diag/03_step.sql_out +17 -0
  76. pg_converter/bundled/packets/test_dba_idx_diag/04_step.sql_out +58 -0
  77. pg_converter/bundled/packets/test_dba_idx_diag/05_step.sql_out +99 -0
  78. pg_converter/bundled/packets/test_dba_idx_diag/06_step.sql_out +31 -0
  79. pg_converter/bundled/packets/test_dba_idx_diag/07_step.sql_out +24 -0
  80. pg_converter/bundled/packets/test_dba_idx_diag/08_step.sql_out +8 -0
  81. pg_converter/bundled/packets/test_dba_idx_diag/09_step.sql_out +16 -0
  82. pg_converter/bundled/packets/test_dba_idx_diag/10_step.sql_out +16 -0
  83. pg_converter/bundled/packets/test_export_data/01_test.sql +22 -0
  84. pg_converter/bundled/packets/test_export_data/02_test.sql +2 -0
  85. pg_converter/bundled/packets/test_export_data/meta_data.json +9 -0
  86. pg_converter/bundled/packets/test_gens/00_step.sql +21 -0
  87. pg_converter/bundled/packets/test_gens/01_gen_nsp.sql +7 -0
  88. pg_converter/bundled/packets/test_gens/01_gen_obj.sql +8 -0
  89. pg_converter/bundled/packets/test_gens/01_step.sql +3 -0
  90. pg_converter/bundled/packets/test_gens/02_gen_nsp.sql +7 -0
  91. pg_converter/bundled/packets/test_gens/02_step.sql +3 -0
  92. pg_converter/bundled/packets/test_gens/03_gen_obj.sql +7 -0
  93. pg_converter/bundled/packets/test_gens/03_step.sql +3 -0
  94. pg_converter/bundled/packets/test_gens/meta_data.json +7 -0
  95. pg_converter/bundled/packets/test_get_version/01_step.sql +1 -0
  96. pg_converter/bundled/packets/test_get_version/meta_data.json +11 -0
  97. pg_converter/bundled/packets/test_insert_delete_select/01_step.sql +5 -0
  98. pg_converter/bundled/packets/test_insert_delete_select/meta_data.json +12 -0
  99. pg_converter/bundled/packets/test_insert_info/01_step.sql +11 -0
  100. pg_converter/bundled/packets/test_insert_info/meta_data.json +11 -0
  101. pg_converter/bundled/packets/test_int4_to_int8/01_step.sql +33 -0
  102. pg_converter/bundled/packets/test_int4_to_int8/02_gen_obj.sql +28 -0
  103. pg_converter/bundled/packets/test_int4_to_int8/02_step.sql +7 -0
  104. pg_converter/bundled/packets/test_int4_to_int8/03_step.sql +5 -0
  105. pg_converter/bundled/packets/test_int4_to_int8/04_step.sql +14 -0
  106. pg_converter/bundled/packets/test_int4_to_int8/05_step.sql +2 -0
  107. pg_converter/bundled/packets/test_int4_to_int8/06_step.sql +16 -0
  108. pg_converter/bundled/packets/test_int4_to_int8/07_step.sql +2 -0
  109. pg_converter/bundled/packets/test_int4_to_int8/meta_data.json +5 -0
  110. pg_converter/bundled/packets/test_int4_to_int8/run_once.sql +12 -0
  111. pg_converter/bundled/packets/test_maint/01_step.sql +1 -0
  112. pg_converter/bundled/packets/test_maint/02_step.sql +1 -0
  113. pg_converter/bundled/packets/test_maint/03_step.sql +5 -0
  114. pg_converter/bundled/packets/test_maint/04_step.sql +1 -0
  115. pg_converter/bundled/packets/test_maint/05_step.sql +3 -0
  116. pg_converter/bundled/packets/test_maint/06_step.sql +1 -0
  117. pg_converter/bundled/packets/test_maint/meta_data.json +12 -0
  118. pg_converter/bundled/packets/test_override_conf_param/01_step.sql +1 -0
  119. pg_converter/bundled/packets/test_override_conf_param/meta_data.json +4 -0
  120. pg_converter/bundled/packets/test_placeholders/00_step.sql +3 -0
  121. pg_converter/bundled/packets/test_placeholders/01_gen_obj.sql +3 -0
  122. pg_converter/bundled/packets/test_placeholders/01_step.sql +3 -0
  123. pg_converter/bundled/packets/test_placeholders/meta_data.json +3 -0
  124. pg_converter/bundled/packets/test_prepare_dbs/01_gen_obj.sql +25 -0
  125. pg_converter/bundled/packets/test_prepare_dbs/01_step.sql +1 -0
  126. pg_converter/bundled/packets/test_prepare_dbs/meta_data.json +4 -0
  127. pg_converter/bundled/packets/test_py_step/01_step.sql +9 -0
  128. pg_converter/bundled/packets/test_py_step/02_step.py +15 -0
  129. pg_converter/bundled/packets/test_py_step/data/data_a.txt +1 -0
  130. pg_converter/bundled/packets/test_py_step/data/data_b.txt +1 -0
  131. pg_converter/bundled/packets/test_py_step/meta_data.json +5 -0
  132. pg_converter/bundled/packets/test_raise/01_step.sql +8 -0
  133. pg_converter/bundled/packets/test_raise/meta_data.json +10 -0
  134. pg_converter/bundled/packets/test_read_only/01_step.sql +12 -0
  135. pg_converter/bundled/packets/test_read_only/meta_data.json +12 -0
  136. pg_converter/bundled/packets/test_read_only_exception/01_step.sql +12 -0
  137. pg_converter/bundled/packets/test_read_only_exception/meta_data.json +4 -0
  138. pg_converter/bundled/packets/test_read_only_gens/01_gen_nsp.sql +7 -0
  139. pg_converter/bundled/packets/test_read_only_gens/01_gen_obj.sql +8 -0
  140. pg_converter/bundled/packets/test_read_only_gens/01_step.sql +3 -0
  141. pg_converter/bundled/packets/test_read_only_gens/02_gen_nsp.sql +7 -0
  142. pg_converter/bundled/packets/test_read_only_gens/02_step.sql +3 -0
  143. pg_converter/bundled/packets/test_read_only_gens/03_gen_obj.sql +7 -0
  144. pg_converter/bundled/packets/test_read_only_gens/03_step.sql +3 -0
  145. pg_converter/bundled/packets/test_read_only_gens/meta_data.json +7 -0
  146. pg_converter/bundled/packets/test_read_only_raise/01_step.sql +8 -0
  147. pg_converter/bundled/packets/test_read_only_raise/meta_data.json +10 -0
  148. pg_converter/bundled/packets/test_skip_action_cancel/01_step.sql +5 -0
  149. pg_converter/bundled/packets/test_skip_action_cancel/02_gen_obj.sql +8 -0
  150. pg_converter/bundled/packets/test_skip_action_cancel/02_step.sql +3 -0
  151. pg_converter/bundled/packets/test_skip_action_cancel/03_step.sql +5 -0
  152. pg_converter/bundled/packets/test_skip_action_cancel/meta_data.json +4 -0
  153. pg_converter/bundled/packets/test_skip_step_cancel/01_step.sql +5 -0
  154. pg_converter/bundled/packets/test_skip_step_cancel/02_step.sql +5 -0
  155. pg_converter/bundled/packets/test_skip_step_cancel/03_step.sql +5 -0
  156. pg_converter/bundled/packets/test_skip_step_cancel/meta_data.json +4 -0
  157. pg_converter/bundled/packets/test_sleep/01_step.sql +5 -0
  158. pg_converter/bundled/packets/test_sleep/meta_data.json +5 -0
  159. pg_converter/bundled/packets/test_sleep_sigint/01_step.sql +5 -0
  160. pg_converter/bundled/packets/test_sleep_sigint/02_step.sql +5 -0
  161. pg_converter/bundled/packets/test_sleep_sigint/meta_data.json +5 -0
  162. pg_converter/bundled/packets/test_syntax_exception/01_step.sql +5 -0
  163. pg_converter/bundled/packets/test_template/02_gen_obj.sql +32 -0
  164. pg_converter/bundled/packets/test_template/02_step.sql +3 -0
  165. pg_converter/bundled/packets/test_template/03_gen_nsp.sql +7 -0
  166. pg_converter/bundled/packets/test_template/03_step.sql +3 -0
  167. pg_converter/bundled/packets/test_unique_exception/01_step.sql +6 -0
  168. pg_converter/bundled/packets/test_unique_exception/meta_data.json +11 -0
  169. pg_converter/bundled/packets/test_wait_tx/01_step.sql +6 -0
  170. pg_converter/bundled/packets/test_wait_tx/02_step.sql +1 -0
  171. pg_converter/bundled/packets/test_wait_tx/03_step.sql +1 -0
  172. pg_converter/bundled/packets/test_wait_tx/meta_data.json +12 -0
  173. pg_converter/cli.py +173 -0
  174. pg_converter/config.py +388 -0
  175. pg_converter/core/__init__.py +103 -0
  176. pg_converter/core/constants.py +114 -0
  177. pg_converter/core/context.py +212 -0
  178. pg_converter/core/database.py +433 -0
  179. pg_converter/core/events.py +70 -0
  180. pg_converter/core/executor.py +813 -0
  181. pg_converter/core/failures.py +269 -0
  182. pg_converter/core/lock_observer.py +396 -0
  183. pg_converter/core/packet.py +440 -0
  184. pg_converter/core/python_executor.py +256 -0
  185. pg_converter/core/session.py +248 -0
  186. pg_converter/core/tracker.py +597 -0
  187. pg_converter/logging_config.py +513 -0
  188. pg_converter/resources.py +26 -0
  189. pg_converter/runner.py +1587 -0
  190. pg_converter/utils/__init__.py +50 -0
  191. pg_converter/utils/common.py +171 -0
  192. pg_converter/utils/export.py +217 -0
  193. pg_converter/utils/signals.py +173 -0
  194. pg_converter/utils/webhooks.py +448 -0
  195. pg_converter-2.0b0.dist-info/METADATA +390 -0
  196. pg_converter-2.0b0.dist-info/RECORD +200 -0
  197. pg_converter-2.0b0.dist-info/WHEEL +5 -0
  198. pg_converter-2.0b0.dist-info/entry_points.txt +2 -0
  199. pg_converter-2.0b0.dist-info/licenses/LICENSE +21 -0
  200. pg_converter-2.0b0.dist-info/top_level.txt +1 -0
@@ -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
+ ]
@@ -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,7 @@
1
+ SELECT
2
+ datname,
3
+ age(datfrozenxid) as datfrozenxid_age,
4
+ mxid_age(datminmxid) as datminmxid_age
5
+ FROM pg_database
6
+ WHERE (age(datfrozenxid) > 1300000000 or mxid_age(datminmxid) > 1400000000)
7
+ ORDER BY age(datfrozenxid) DESC;
@@ -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
+ }