-
Couldn't load subscription status.
- Fork 158
fix: added retry logic for SQL DB connection, updated agent and thread management #625
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
4aa09e8
f2da255
4603e3c
3c51102
4145ad0
2a7aa1b
61b4421
4f6e907
c1d8230
4772ad3
2088121
2cdbaf9
8b5e6d2
e96b8bf
2506157
5edf74b
53dd2a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,6 +8,8 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from backend.common.config import config | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import time | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| load_dotenv() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| driver = config.ODBC_DRIVER | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -33,33 +35,47 @@ def dict_cursor(cursor): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def get_connection(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| credential = DefaultAzureCredential(managed_identity_client_id=mid_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| max_retries = 5 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| retry_delay = 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token_bytes = credential.get_token( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "https://database.windows.net/.default" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).token.encode("utf-16-LE") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token_struct = struct.pack( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"<I{len(token_bytes)}s", len(token_bytes), token_bytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SQL_COPT_SS_ACCESS_TOKEN = ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1256 # This connection option is defined by Microsoft in msodbcsql.h | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for attempt in range(max_retries): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| credential = DefaultAzureCredential(managed_identity_client_id=mid_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Set up the connection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| connection_string = f"DRIVER={driver};SERVER={server};DATABASE={database};" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| conn = pyodbc.connect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| connection_string, attrs_before={SQL_COPT_SS_ACCESS_TOKEN: token_struct} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return conn | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except pyodbc.Error as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.error(f"Failed with Default Credential: {str(e)}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| conn = pyodbc.connect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"DRIVER={driver};SERVER={server};DATABASE={database};UID={username};PWD={password}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timeout=5, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.info("Connected using Username & Password") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return conn | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token_bytes = credential.get_token( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "https://database.windows.net/.default" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).token.encode("utf-16-LE") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token_struct = struct.pack( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"<I{len(token_bytes)}s", len(token_bytes), token_bytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SQL_COPT_SS_ACCESS_TOKEN = ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1256 # This connection option is defined by Microsoft in msodbcsql.h | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Set up the connection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| connection_string = f"DRIVER={driver};SERVER={server};DATABASE={database};" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| conn = pyodbc.connect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| connection_string, attrs_before={SQL_COPT_SS_ACCESS_TOKEN: token_struct} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return conn | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except pyodbc.Error as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.error(f"Failed with Default Credential: {str(e)}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| conn = pyodbc.connect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"DRIVER={driver};SERVER={server};DATABASE={database};UID={username};PWD={password}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timeout=5, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.info("Connected using Username & Password") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return conn | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except pyodbc.Error as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.error(f"Failed with Username & Password: {str(e)}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if attempt < max_retries - 1: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.info(f"Retrying in {retry_delay} seconds...") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| time.sleep(retry_delay) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| retry_delay *= 2 # Exponential backoff | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise e | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+63
to
80
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | |
| conn = pyodbc.connect( | |
| f"DRIVER={driver};SERVER={server};DATABASE={database};UID={username};PWD={password}", | |
| timeout=5, | |
| ) | |
| logging.info("Connected using Username & Password") | |
| return conn | |
| except pyodbc.Error as e: | |
| logging.error(f"Failed with Username & Password: {str(e)}") | |
| if attempt < max_retries - 1: | |
| logging.info(f"Retrying in {retry_delay} seconds...") | |
| time.sleep(retry_delay) | |
| retry_delay *= 2 # Exponential backoff | |
| else: | |
| raise e | |
| try: | |
| conn = connect_with_password() | |
| logging.info("Connected using Username & Password") | |
| return conn | |
| except pyodbc.Error as e: | |
| logging.error(f"Failed with Username & Password: {str(e)}") | |
| if attempt < max_retries - 1: | |
| logging.info(f"Retrying in {retry_delay} seconds...") | |
| time.sleep(retry_delay) | |
| retry_delay *= 2 # Exponential backoff | |
| else: | |
| raise e |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -42,20 +42,33 @@ export const getpbi = async (): Promise<string> => { | |||||||
| return ''; | ||||||||
| } | ||||||||
|
|
||||||||
| const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); | ||||||||
|
|
||||||||
| export const getUsers = async (): Promise<User[]> => { | ||||||||
| try { | ||||||||
| const response = await fetch('/api/users'); | ||||||||
| if (!response.ok) { | ||||||||
| throw new Error(`Failed to fetch users: ${response.statusText}`); | ||||||||
| const maxRetries = 1; | ||||||||
| for (let attempt = 0; attempt <= maxRetries; attempt++) { | ||||||||
| try { | ||||||||
| const response = await fetch('/api/users', { | ||||||||
| signal: AbortSignal.timeout(60000) | ||||||||
| }); | ||||||||
| if (!response.ok) { | ||||||||
| throw new Error(`Failed to fetch users: ${response.statusText}`); | ||||||||
| } | ||||||||
| const data: User[] = await response.json(); | ||||||||
| console.log('Fetched users:', data); | ||||||||
| return data; | ||||||||
| } catch (error) { | ||||||||
| if (attempt < maxRetries && | ||||||||
| error instanceof Error) { | ||||||||
|
Comment on lines
+61
to
+62
|
||||||||
| if (attempt < maxRetries && | |
| error instanceof Error) { | |
| if (attempt < maxRetries) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exception being raised here (
e) is from the inner try-catch block (Username & Password authentication failure), but if the Default Credential also failed, that error information is lost. Consider raising a more comprehensive error that includes both failure modes or the original Default Credential error.