Skip to content

Commit 61b4421

Browse files
fix: Implement retry logic for database connection and user fetching with exponential backoff (#623)
1 parent 2a7aa1b commit 61b4421

File tree

2 files changed

+65
-36
lines changed

2 files changed

+65
-36
lines changed

src/App/backend/services/sqldb_service.py

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
from backend.common.config import config
1010

11+
import time
12+
1113
load_dotenv()
1214

1315
driver = config.ODBC_DRIVER
@@ -33,33 +35,47 @@ def dict_cursor(cursor):
3335

3436

3537
def get_connection():
36-
try:
37-
credential = DefaultAzureCredential(managed_identity_client_id=mid_id)
38+
max_retries = 5
39+
retry_delay = 2
3840

39-
token_bytes = credential.get_token(
40-
"https://database.windows.net/.default"
41-
).token.encode("utf-16-LE")
42-
token_struct = struct.pack(
43-
f"<I{len(token_bytes)}s", len(token_bytes), token_bytes
44-
)
45-
SQL_COPT_SS_ACCESS_TOKEN = (
46-
1256 # This connection option is defined by Microsoft in msodbcsql.h
47-
)
41+
for attempt in range(max_retries):
42+
try:
43+
credential = DefaultAzureCredential(managed_identity_client_id=mid_id)
4844

49-
# Set up the connection
50-
connection_string = f"DRIVER={driver};SERVER={server};DATABASE={database};"
51-
conn = pyodbc.connect(
52-
connection_string, attrs_before={SQL_COPT_SS_ACCESS_TOKEN: token_struct}
53-
)
54-
return conn
55-
except pyodbc.Error as e:
56-
logging.error(f"Failed with Default Credential: {str(e)}")
57-
conn = pyodbc.connect(
58-
f"DRIVER={driver};SERVER={server};DATABASE={database};UID={username};PWD={password}",
59-
timeout=5,
60-
)
61-
logging.info("Connected using Username & Password")
62-
return conn
45+
token_bytes = credential.get_token(
46+
"https://database.windows.net/.default"
47+
).token.encode("utf-16-LE")
48+
token_struct = struct.pack(
49+
f"<I{len(token_bytes)}s", len(token_bytes), token_bytes
50+
)
51+
SQL_COPT_SS_ACCESS_TOKEN = (
52+
1256 # This connection option is defined by Microsoft in msodbcsql.h
53+
)
54+
55+
# Set up the connection
56+
connection_string = f"DRIVER={driver};SERVER={server};DATABASE={database};"
57+
conn = pyodbc.connect(
58+
connection_string, attrs_before={SQL_COPT_SS_ACCESS_TOKEN: token_struct}
59+
)
60+
return conn
61+
except pyodbc.Error as e:
62+
logging.error(f"Failed with Default Credential: {str(e)}")
63+
try:
64+
conn = pyodbc.connect(
65+
f"DRIVER={driver};SERVER={server};DATABASE={database};UID={username};PWD={password}",
66+
timeout=5,
67+
)
68+
logging.info("Connected using Username & Password")
69+
return conn
70+
except pyodbc.Error as e:
71+
logging.error(f"Failed with Username & Password: {str(e)}")
72+
73+
if attempt < max_retries - 1:
74+
logging.info(f"Retrying in {retry_delay} seconds...")
75+
time.sleep(retry_delay)
76+
retry_delay *= 2 # Exponential backoff
77+
else:
78+
raise e
6379

6480

6581
def get_client_name_from_db(client_id: str) -> str:

src/App/frontend/src/api/api.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,33 @@ export const getpbi = async (): Promise<string> => {
4242
return '';
4343
}
4444

45+
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
46+
4547
export const getUsers = async (): Promise<User[]> => {
46-
try {
47-
const response = await fetch('/api/users');
48-
if (!response.ok) {
49-
throw new Error(`Failed to fetch users: ${response.statusText}`);
48+
const maxRetries = 1;
49+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
50+
try {
51+
const response = await fetch('/api/users', {
52+
signal: AbortSignal.timeout(60000)
53+
});
54+
if (!response.ok) {
55+
throw new Error(`Failed to fetch users: ${response.statusText}`);
56+
}
57+
const data: User[] = await response.json();
58+
console.log('Fetched users:', data);
59+
return data;
60+
} catch (error) {
61+
if (attempt < maxRetries &&
62+
error instanceof Error) {
63+
console.warn(`Retrying fetch users... (retry ${attempt + 1}/${maxRetries})`);
64+
await sleep(5000); // Simple 5 second delay
65+
} else {
66+
console.error('Error fetching users:', error);
67+
return [];
68+
}
5069
}
51-
const data: User[] = await response.json();
52-
console.log('Fetched users:', data);
53-
return data;
54-
} catch (error) {
55-
console.error('Error fetching users:', error);
56-
return [];
57-
// throw error;
5870
}
71+
return [];
5972
};
6073

6174
// export const fetchChatHistoryInit = async (): Promise<Conversation[] | null> => {

0 commit comments

Comments
 (0)