Hello Simon, I tried your suggestion as POC:
def setup_event_handlers(engine): @event.listens_for(engine, 'do_connect') def receive_do_connect(dialect, conn_rec, cargs, cparams): print("inside do_connect") print('password %s' % cparams['password']) try: print("inside try") return cx_Oracle.connect(*cargs, **cparams) except Exception as e: print("inside catch") cparams['password'] = "NewPassword" return cx_Oracle.connect(*cargs, **cparams) Every time except block is triggered even I set correct password in Except block. As per document once cparams password set it should pass new password for new request. Can you please suggest if I miss anything here? Regards, Pydi On Tuesday, March 22, 2022 at 9:33:57 AM UTC-7 Srinu Chp wrote: > Hello Simon, > > Thank you very much for detail information. > > Regards, > Pydi > > On Tuesday, March 22, 2022 at 3:11:34 AM UTC-7 Simon King wrote: > >> I don't know anything about Airflow. Are you sure that each of these >> tasks is running inside the same Python interpreter/process? I see >> Airflow can distribute tasks among workers: >> >> https://airflow.apache.org/docs/apache-airflow/stable/executor/index.html >> >> This sounds like a problem that is going to be very specific to your >> deployment environment. If you have multiple worker processes, you're >> going to need some way to distribute the new password to each of the >> workers (eg. a shared cache) >> >> But regardless of that, you're still not following the pattern from >> the documentation. I don't understand why you are creating a new >> engine inside your do_connect handler. You should be creating a >> cx-Oracle connection and returning it. The parameters passed to the >> do_connect handler have already been parsed out of the connection >> string. So for example, if your connection string includes a >> "password=some_password" parameter, then cparams will have a >> "password" key with the value "some_password". The same cparams >> dictionary will be passed to the do_connect handler each time, so if >> you mutate the dictionary (eg. by updating the "password" key), the >> next call to the handler will contain the new value. >> >> If each task invocation is creating a new engine using a connection >> string that is out of date, then none of that will help you, but that >> would be an Airflow problem, not an SQLAlchemy problem. >> >> Simon >> >> On Mon, Mar 21, 2022 at 8:27 PM Srinu Chp <chps...@gmail.com> wrote: >> > >> > Hello Simon, >> > >> > I tried POC approach in my project where we are using Airflow using >> sqlalchemy to connect to db. Here is the event code: >> > >> > @event.listens_for(engine, "do_connect") >> > def receive_do_connect(dialect, conn_rec, cargs, cparams): >> > global SQL_ALCHEMY_CONN >> > log.info("receive_do_connect called for user AIRFLOW.") >> > log.info("user details DB: {}".format(SQL_ALCHEMY_CONN)) >> > # creating new engine to valide using cx_oracle driver >> > engine_new = >> create_engine(f'{SQL_ALCHEMY_CONN}/?encoding=UTF-8&nencoding=UTF-8', >> max_identifier_length=128) >> > try: >> > with engine_new.connect() as conn: >> > log.info(conn.scalar("select sysdate from dual")) >> > SQL_ALCHEMY_CONN = "testNew_try" >> > except Exception as e: >> > # check for invalid user/pwd error >> > if search('ORA-01017', str(e)): >> > log.info("receive_do_connect exception occurred during engine >> connection e: {}".format(e)) >> > ...<connect to secret client and fetch new password> >> > //update with new password >> > SQL_ALCHEMY_CONN = "testNew_except" >> > # this log print new value with updated password >> > log.info("user details DB after update in except block: >> {}".format(SQL_ALCHEMY_CONN)) >> > cparams['New password'] >> > >> > global SQL_ALCHEMY_CONN value is set during initialization. Once >> password is rotated I am trying to update the SQL_ALCHEMY_CONN so that next >> request will not go in except block. Every time logs print old >> SQL_ALCHEMY_CONN value even value is update in except block. >> > >> > second approach: >> > I tried to set env variable in except block: >> > os.environ['AIRFLOW__CORE__SQL_ALCHEMY_CONN'] >> > env variable also refer to old value even after updating in except >> block. >> > >> > Can you please suggestion? >> > Regards, >> > Pydi >> > On Monday, March 21, 2022 at 10:55:09 AM UTC-7 Srinu Chp wrote: >> >> >> >> Hello Simon, >> >> >> >> Perfect, working as expected in standalone POC. Thank you quick help >> >> >> >> Regards, >> >> Pydi >> >> >> >> On Monday, March 21, 2022 at 9:52:04 AM UTC-7 Simon King wrote: >> >>> >> >>> As suggested here: >> >>> >> >>> >> https://docs.sqlalchemy.org/en/14/core/engines.html#fully-replacing-the-dbapi-connect-function >> >>> >> >>> In your do_connect handler, rather than calling engine.connect(), you >> >>> need to call cx_Oracle.connect(), and return the result. You can wrap >> >>> this in an exception handler that detects the "incorrect password" >> >>> error to fetch new credentials. Something like this perhaps: >> >>> >> >>> @event.listens_for(engine, 'do_connect') >> >>> def receive_do_connect(dialect, conn_rec, cargs, cparams): >> >>> try: >> >>> return cx_Oracle.connect(*cargs, **cparams) >> >>> except <appropriate-cx-oracle-error>: >> >>> cparams["password"] = get_new_password() >> >>> return cx_Oracle.connect(*args, **cparams) >> >>> >> >>> Hope that helps, >> >>> >> >>> Simon >> >>> >> >>> On Mon, Mar 21, 2022 at 4:26 PM Srinu Chp <chps...@gmail.com> wrote: >> >>> > >> >>> > Hello Simon, >> >>> > >> >>> > Thank you for prompt response. I really appreciate your help. I am >> trying to achieve password rotation and we are using secret client to fetch >> new password. I tried do_connect event and fetch new password from secret >> client, working as expected but we are facing performance issue as we are >> every time connecting to secret client(3~5sec for each request). Instead I >> am trying to achieve if connect fails then fetch from secret client. >> >>> > >> >>> > I tried with handle_error event, when i get error check for invalid >> user/pwd and update session with latest engine. This approach also did not >> help >> >>> > >> >>> > Any insights are highly appreciated. Please suggest best approach. >> >>> > >> >>> > Regards, >> >>> > Pydi >> >>> > On Monday, March 21, 2022 at 2:22:04 AM UTC-7 Simon King wrote: >> >>> >> >> >>> >> I don't really understand what's going on in your code, but you >> seem >> >>> >> to be calling engine.connect() inside your "do_connect" event >> handler. >> >>> >> I would expect that to trigger another "do_connect" event, which in >> >>> >> turn will call engine.connect() again, which will trigger another >> >>> >> "do_connect" event, and so on. I'm surprised the application gets >> as >> >>> >> far as it does. Maybe the exception handler inside >> receive_do_connect >> >>> >> is allowing it to stumble on. >> >>> >> >> >>> >> Simon >> >>> >> >> >>> >> On Mon, Mar 21, 2022 at 4:51 AM Srinu Chp <chps...@gmail.com> >> wrote: >> >>> >> > >> >>> >> > Hello Team, >> >>> >> > >> >>> >> > I tried to create a standalone application POC for sqlalchemy to >> db connection. When I registered do_connect event, I see event is triggered >> couple of times for one call: >> >>> >> > sqlalchemy_connection.py >> >>> >> > >> >>> >> > import os >> >>> >> > import cx_Oracle >> >>> >> > from sqlalchemy import create_engine >> >>> >> > from sqlalchemy import event >> >>> >> > >> >>> >> > cx_Oracle.init_oracle_client(lib_dir=os.environ.get("HOME") + >> "/Downloads/instantclient_19_8") >> >>> >> > SQLALCHEMY_CONN = "test" >> >>> >> > count = 0 >> >>> >> > engine = None >> >>> >> > def connect_db(pwd): >> >>> >> > global count >> >>> >> > global engine >> >>> >> > print(SQLALCHEMY_CONN) >> >>> >> > username = "ADMIN" >> >>> >> > password = pwd >> >>> >> > dsn = "pydidb_high" >> >>> >> > engine = create_engine( >> >>> >> > >> f'oracle://{username}:{password}@{dsn}/?encoding=UTF-8&nencoding=UTF-8', >> max_identifier_length=128) >> >>> >> > setup_event_handlers(engine) >> >>> >> > >> >>> >> > def setup_event_handlers(engine): >> >>> >> > @event.listens_for(engine, 'do_connect') >> >>> >> > def receive_do_connect(dialect, conn_rec, cargs, cparams): >> >>> >> > print("inside do_connect") >> >>> >> > global count >> >>> >> > try: >> >>> >> > with engine.connect() as conn: >> >>> >> > print("inside do_connect try block") >> >>> >> > print(conn.scalar("select sysdate from dual")) >> >>> >> > count += 2 >> >>> >> > except Exception as e: >> >>> >> > print("inside do_connect except block") >> >>> >> > count += 1 >> >>> >> > >> >>> >> > def db_connect_test(): >> >>> >> > print(engine) >> >>> >> > with engine.connect() as conn: >> >>> >> > print(conn.scalar("select sysdate from dual")) >> >>> >> > >> >>> >> > gevent_sync.py >> >>> >> > >> >>> >> > import gevent >> >>> >> > import random >> >>> >> > import sqlalchemy_connection >> >>> >> > >> >>> >> > def task(pid): >> >>> >> > gevent.sleep(random.randint(0,2)*0.001) >> >>> >> > print('Task %s done' % pid) >> >>> >> > sqlalchemy_connection.connect_db(**********) >> >>> >> > sqlalchemy_connection.db_connect_test() >> >>> >> > >> >>> >> > def synchronous(): >> >>> >> > for i in range(1,2): >> >>> >> > task(i) >> >>> >> > # sqlalchemy_connection.connect_db(**********) >> >>> >> > >> >>> >> > def asynchronous(): >> >>> >> > threads = [gevent.spawn(task, i) for i in range(2)] >> >>> >> > gevent.joinall(threads) >> >>> >> > >> >>> >> > print('Synchronous:') >> >>> >> > synchronous() >> >>> >> > print('count %s ' % sqlalchemy_connection.count) >> >>> >> > >> >>> >> > # print('Asynchronous:') >> >>> >> > # asynchronous() >> >>> >> > # print('count %s' % sqlalchemy_connection.count) >> >>> >> > >> >>> >> > Output: >> >>> >> > >> >>> >> > Synchronous: >> >>> >> > Task 1 done >> >>> >> > test >> >>> >> > >> Engine(oracle://ADMIN:***@pydidb_high/?encoding=UTF-8&nencoding=UTF-8) >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect >> >>> >> > inside do_connect except block >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:39:47 >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:39:49 >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:39:51 >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:39:54 >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:39:56 >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:39:59 >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:40:01 >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:40:04 >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:40:09 >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:40:15 >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:40:17 >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:40:19 >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:40:21 >> >>> >> > inside do_connect try block >> >>> >> > 2022-03-21 04:40:24 >> >>> >> > 2022-03-21 04:40:26 >> >>> >> > count 29 >> >>> >> > >> >>> >> > highly appreciate any inputs. >> >>> >> > Regards, >> >>> >> > Pydi >> >>> >> > >> >>> >> > -- >> >>> >> > SQLAlchemy - >> >>> >> > The Python SQL Toolkit and Object Relational Mapper >> >>> >> > >> >>> >> > http://www.sqlalchemy.org/ >> >>> >> > >> >>> >> > To post example code, please provide an MCVE: Minimal, Complete, >> and Verifiable Example. See http://stackoverflow.com/help/mcve for a >> full description. >> >>> >> > --- >> >>> >> > You received this message because you are subscribed to the >> Google Groups "sqlalchemy" group. >> >>> >> > To unsubscribe from this group and stop receiving emails from >> it, send an email to sqlalchemy+...@googlegroups.com. >> >>> >> > To view this discussion on the web visit >> https://groups.google.com/d/msgid/sqlalchemy/08096636-06c4-478f-a54d-0bc8f71db414n%40googlegroups.com >> . >> >>> > >> >>> > -- >> >>> > SQLAlchemy - >> >>> > The Python SQL Toolkit and Object Relational Mapper >> >>> > >> >>> > http://www.sqlalchemy.org/ >> >>> > >> >>> > To post example code, please provide an MCVE: Minimal, Complete, >> and Verifiable Example. See http://stackoverflow.com/help/mcve for a >> full description. >> >>> > --- >> >>> > You received this message because you are subscribed to the Google >> Groups "sqlalchemy" group. >> >>> > To unsubscribe from this group and stop receiving emails from it, >> send an email to sqlalchemy+...@googlegroups.com. >> >>> > To view this discussion on the web visit >> https://groups.google.com/d/msgid/sqlalchemy/a0d30530-fc23-4e70-8965-a7b10c17a65cn%40googlegroups.com >> . >> > >> > -- >> > SQLAlchemy - >> > The Python SQL Toolkit and Object Relational Mapper >> > >> > http://www.sqlalchemy.org/ >> > >> > To post example code, please provide an MCVE: Minimal, Complete, and >> Verifiable Example. See http://stackoverflow.com/help/mcve for a full >> description. >> > --- >> > You received this message because you are subscribed to the Google >> Groups "sqlalchemy" group. >> > To unsubscribe from this group and stop receiving emails from it, send >> an email to sqlalchemy+...@googlegroups.com. >> > To view this discussion on the web visit >> https://groups.google.com/d/msgid/sqlalchemy/136a743f-e4fb-4cde-b363-9af7670057b8n%40googlegroups.com >> . >> > -- SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper http://www.sqlalchemy.org/ To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description. --- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/sqlalchemy/b3b16b71-ef90-4764-8e95-2452acd15a5fn%40googlegroups.com.