trovesuite 1.0.28__tar.gz → 1.0.29__tar.gz
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.
- {trovesuite-1.0.28/src/trovesuite.egg-info → trovesuite-1.0.29}/PKG-INFO +1 -1
- {trovesuite-1.0.28 → trovesuite-1.0.29}/pyproject.toml +2 -2
- {trovesuite-1.0.28 → trovesuite-1.0.29}/setup.py +1 -1
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/__init__.py +1 -1
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/configs/database.py +114 -11
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/configs/settings.py +0 -5
- {trovesuite-1.0.28 → trovesuite-1.0.29/src/trovesuite.egg-info}/PKG-INFO +1 -1
- {trovesuite-1.0.28 → trovesuite-1.0.29}/LICENSE +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/MANIFEST.in +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/README.md +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/requirements.txt +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/setup.cfg +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/auth/__init__.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/auth/auth_base.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/auth/auth_controller.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/auth/auth_read_dto.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/auth/auth_service.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/auth/auth_write_dto.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/configs/__init__.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/configs/logging.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/entities/__init__.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/entities/health.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/entities/sh_response.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/notification/__init__.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/notification/notification_base.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/notification/notification_controller.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/notification/notification_read_dto.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/notification/notification_service.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/notification/notification_write_dto.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/storage/__init__.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/storage/storage_base.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/storage/storage_controller.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/storage/storage_read_dto.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/storage/storage_service.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/storage/storage_write_dto.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/utils/__init__.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/utils/helper.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/utils/templates.py +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite.egg-info/SOURCES.txt +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite.egg-info/dependency_links.txt +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite.egg-info/not-zip-safe +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite.egg-info/requires.txt +0 -0
- {trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trovesuite
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.29
|
|
4
4
|
Summary: TroveSuite services package providing authentication, authorization, notifications, Azure Storage, and other enterprise services for TroveSuite applications
|
|
5
5
|
Home-page: https://dev.azure.com/brightgclt/trovesuite/_git/packages
|
|
6
6
|
Author: Bright Debrah Owusu
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "trovesuite"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.29"
|
|
8
8
|
description = "TroveSuite services package providing authentication, authorization, notifications, Azure Storage, and other enterprise services for TroveSuite applications"
|
|
9
9
|
authors = ["brightgclt <brightgclt@gmail.com>"]
|
|
10
10
|
license = "MIT"
|
|
@@ -58,7 +58,7 @@ Documentation = "https://dev.azure.com/brightgclt/trovesuite/_git/packages"
|
|
|
58
58
|
|
|
59
59
|
[project]
|
|
60
60
|
name = "trovesuite"
|
|
61
|
-
version = "1.0.
|
|
61
|
+
version = "1.0.29"
|
|
62
62
|
description = "TroveSuite services package providing authentication, authorization, notifications, Azure Storage, and other enterprise services for TroveSuite applications"
|
|
63
63
|
readme = "README.md"
|
|
64
64
|
license = {text = "MIT"}
|
|
@@ -15,7 +15,7 @@ with open("pyproject.toml", "r", encoding="utf-8") as fh:
|
|
|
15
15
|
|
|
16
16
|
setup(
|
|
17
17
|
name="trovesuite",
|
|
18
|
-
version="1.0.
|
|
18
|
+
version="1.0.29",
|
|
19
19
|
author="Bright Debrah Owusu",
|
|
20
20
|
author_email="owusu.debrah@deladetech.com",
|
|
21
21
|
description="TroveSuite services package providing authentication, authorization, notifications, and other enterprise services for TroveSuite applications",
|
|
@@ -6,6 +6,7 @@ from typing import Generator, Optional
|
|
|
6
6
|
import psycopg2
|
|
7
7
|
import psycopg2.pool
|
|
8
8
|
from psycopg2.extras import RealDictCursor
|
|
9
|
+
import threading
|
|
9
10
|
from .settings import db_settings
|
|
10
11
|
from .logging import get_logger
|
|
11
12
|
|
|
@@ -13,6 +14,7 @@ logger = get_logger("database")
|
|
|
13
14
|
|
|
14
15
|
# Database connection pool
|
|
15
16
|
_connection_pool: Optional[psycopg2.pool.ThreadedConnectionPool] = None
|
|
17
|
+
_initialization_lock = threading.Lock()
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
class DatabaseConfig:
|
|
@@ -91,6 +93,15 @@ db_config = DatabaseConfig()
|
|
|
91
93
|
def initialize_database():
|
|
92
94
|
"""Initialize database connections and pool"""
|
|
93
95
|
global _connection_pool
|
|
96
|
+
|
|
97
|
+
# Close existing pool if it exists (cleanup before reinitializing)
|
|
98
|
+
if _connection_pool is not None:
|
|
99
|
+
try:
|
|
100
|
+
_connection_pool.closeall()
|
|
101
|
+
logger.info("Closed existing connection pool before reinitialization")
|
|
102
|
+
except Exception as e:
|
|
103
|
+
logger.warning(f"Error closing existing pool: {str(e)}")
|
|
104
|
+
_connection_pool = None
|
|
94
105
|
|
|
95
106
|
try:
|
|
96
107
|
# Test connection first
|
|
@@ -99,39 +110,131 @@ def initialize_database():
|
|
|
99
110
|
|
|
100
111
|
# Create connection pool
|
|
101
112
|
_connection_pool = db_config.create_connection_pool()
|
|
113
|
+
|
|
114
|
+
# Verify pool was created successfully
|
|
115
|
+
if _connection_pool is None:
|
|
116
|
+
raise Exception("Connection pool creation returned None")
|
|
102
117
|
|
|
103
|
-
logger.info("Database initialization completed successfully")
|
|
118
|
+
logger.info("✅ Database initialization completed successfully")
|
|
104
119
|
|
|
105
120
|
except Exception as e:
|
|
106
|
-
logger.error(f"Database initialization failed: {str(e)}")
|
|
121
|
+
logger.error(f"❌ Database initialization failed: {str(e)}")
|
|
122
|
+
_connection_pool = None # Ensure pool is None on failure
|
|
107
123
|
raise
|
|
108
124
|
|
|
109
125
|
|
|
126
|
+
def _is_pool_valid(pool) -> bool:
|
|
127
|
+
"""Check if the connection pool is valid and usable"""
|
|
128
|
+
if pool is None:
|
|
129
|
+
return False
|
|
130
|
+
try:
|
|
131
|
+
# ThreadedConnectionPool doesn't expose a direct "closed" attribute
|
|
132
|
+
# Check if pool has the necessary internal structures
|
|
133
|
+
if not hasattr(pool, '_pool'):
|
|
134
|
+
return False
|
|
135
|
+
if pool._pool is None:
|
|
136
|
+
return False
|
|
137
|
+
# Additional check: verify pool has connection parameters
|
|
138
|
+
if not hasattr(pool, '_kwargs'):
|
|
139
|
+
return False
|
|
140
|
+
return True
|
|
141
|
+
except (AttributeError, Exception) as e:
|
|
142
|
+
logger.debug(f"Pool validation check: {str(e)}")
|
|
143
|
+
return False
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _recover_connection_pool() -> bool:
|
|
147
|
+
"""Attempt to recover the connection pool with retry logic"""
|
|
148
|
+
global _connection_pool, _initialization_lock
|
|
149
|
+
import time
|
|
150
|
+
|
|
151
|
+
with _initialization_lock:
|
|
152
|
+
# Double-check after acquiring lock
|
|
153
|
+
if _connection_pool is not None and _is_pool_valid(_connection_pool):
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
# Close invalid pool if it exists
|
|
157
|
+
if _connection_pool is not None:
|
|
158
|
+
try:
|
|
159
|
+
_connection_pool.closeall()
|
|
160
|
+
logger.info("Closed invalid connection pool")
|
|
161
|
+
except Exception as e:
|
|
162
|
+
logger.warning(f"Error closing invalid pool: {str(e)}")
|
|
163
|
+
_connection_pool = None
|
|
164
|
+
|
|
165
|
+
# Retry with exponential backoff
|
|
166
|
+
max_retries = 3
|
|
167
|
+
base_delay = 1 # Start with 1 second
|
|
168
|
+
|
|
169
|
+
for attempt in range(1, max_retries + 1):
|
|
170
|
+
try:
|
|
171
|
+
logger.warning(f"Attempting to reinitialize connection pool (attempt {attempt}/{max_retries})...")
|
|
172
|
+
initialize_database()
|
|
173
|
+
|
|
174
|
+
if _connection_pool is not None and _is_pool_valid(_connection_pool):
|
|
175
|
+
logger.info(f"✅ Connection pool reinitialized successfully (attempt {attempt})")
|
|
176
|
+
return True
|
|
177
|
+
else:
|
|
178
|
+
logger.warning(f"Pool initialized but validation failed (attempt {attempt})")
|
|
179
|
+
|
|
180
|
+
except Exception as e:
|
|
181
|
+
logger.error(f"Pool reinitialization attempt {attempt} failed: {str(e)}")
|
|
182
|
+
if attempt < max_retries:
|
|
183
|
+
delay = base_delay * (2 ** (attempt - 1)) # Exponential backoff: 1s, 2s, 4s
|
|
184
|
+
logger.info(f"Retrying in {delay} seconds...")
|
|
185
|
+
time.sleep(delay)
|
|
186
|
+
|
|
187
|
+
logger.error("❌ Failed to reinitialize connection pool after all retries")
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
|
|
110
191
|
def get_connection_pool() -> psycopg2.pool.ThreadedConnectionPool:
|
|
111
|
-
"""Get the database connection pool"""
|
|
192
|
+
"""Get the database connection pool, with automatic reinitialization if needed"""
|
|
112
193
|
global _connection_pool
|
|
113
|
-
|
|
194
|
+
|
|
195
|
+
# Fast path: pool exists and is valid
|
|
196
|
+
if _connection_pool is not None and _is_pool_valid(_connection_pool):
|
|
197
|
+
return _connection_pool
|
|
198
|
+
|
|
199
|
+
# Pool is None or invalid, attempt recovery
|
|
200
|
+
if not _recover_connection_pool():
|
|
114
201
|
error_msg = (
|
|
115
|
-
"Database
|
|
116
|
-
"1.
|
|
117
|
-
"2.
|
|
118
|
-
"3. Database
|
|
119
|
-
"4.
|
|
120
|
-
"
|
|
202
|
+
"Database connection pool is unavailable. This usually means:\n"
|
|
203
|
+
"1. Database server is unreachable or down\n"
|
|
204
|
+
"2. Network connectivity issues\n"
|
|
205
|
+
"3. Database credentials are incorrect\n"
|
|
206
|
+
"4. Connection pool exhausted or closed\n"
|
|
207
|
+
"5. Database initialization failed\n"
|
|
208
|
+
"Please check the startup logs and database status."
|
|
121
209
|
)
|
|
122
210
|
logger.error(error_msg)
|
|
123
211
|
raise Exception(error_msg)
|
|
212
|
+
|
|
213
|
+
if _connection_pool is None:
|
|
214
|
+
error_msg = "Connection pool recovery completed but pool is still None"
|
|
215
|
+
logger.error(error_msg)
|
|
216
|
+
raise Exception(error_msg)
|
|
217
|
+
|
|
124
218
|
return _connection_pool
|
|
125
219
|
|
|
126
220
|
|
|
127
221
|
def _validate_connection(conn) -> bool:
|
|
128
222
|
"""Validate if a connection is still alive"""
|
|
129
223
|
try:
|
|
224
|
+
# Check if connection is closed first
|
|
225
|
+
if conn.closed:
|
|
226
|
+
return False
|
|
227
|
+
|
|
130
228
|
# Test if connection is alive with a simple query
|
|
131
229
|
with conn.cursor() as cursor:
|
|
132
230
|
cursor.execute("SELECT 1")
|
|
231
|
+
cursor.fetchone()
|
|
133
232
|
return True
|
|
134
|
-
except (psycopg2.OperationalError, psycopg2.InterfaceError):
|
|
233
|
+
except (psycopg2.OperationalError, psycopg2.InterfaceError, psycopg2.DatabaseError) as e:
|
|
234
|
+
logger.warning(f"Connection validation failed: {str(e)}")
|
|
235
|
+
return False
|
|
236
|
+
except Exception as e:
|
|
237
|
+
logger.warning(f"Unexpected error during connection validation: {str(e)}")
|
|
135
238
|
return False
|
|
136
239
|
|
|
137
240
|
|
|
@@ -10,11 +10,6 @@ class Settings:
|
|
|
10
10
|
DB_PORT: str = os.getenv("DB_PORT")
|
|
11
11
|
DB_PASSWORD: str = os.getenv("DB_PASSWORD")
|
|
12
12
|
|
|
13
|
-
# CORS Endpoint
|
|
14
|
-
# LOCAL_HOST_URL = os.getenv("LOCAL_HOST_URL")
|
|
15
|
-
# FRONTEND_SERVER_URL_1 = os.getenv("FRONTEND_SERVER_URL")
|
|
16
|
-
# FRONTEND_SERVER_URL_2 = os.getenv("FRONTEND_SERVER_URL")
|
|
17
|
-
|
|
18
13
|
# Application settings
|
|
19
14
|
DEBUG: bool = os.getenv("DEBUG", "True").lower() in ("true",1)
|
|
20
15
|
APP_NAME: str = os.getenv("APP_NAME", "Python Template API")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trovesuite
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.29
|
|
4
4
|
Summary: TroveSuite services package providing authentication, authorization, notifications, Azure Storage, and other enterprise services for TroveSuite applications
|
|
5
5
|
Home-page: https://dev.azure.com/brightgclt/trovesuite/_git/packages
|
|
6
6
|
Author: Bright Debrah Owusu
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/notification/notification_controller.py
RENAMED
|
File without changes
|
{trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/notification/notification_read_dto.py
RENAMED
|
File without changes
|
|
File without changes
|
{trovesuite-1.0.28 → trovesuite-1.0.29}/src/trovesuite/notification/notification_write_dto.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|