SqlAlchemy & pyMysql connection pooling on a lambda with multiple DB connections - python

So the issue is I have multiple databases that I want to use the same Database Pool in SqlAlchemy. This resides on a Lambda and the pool is created upon initiation of the Lambda. I want the subsequent DB connections to use the existing pool.
What works just fine is the initial pool connection bpConnect and any subsequent queries to that connection.
What DOESN'T work is the companyConnect connection. I get the following error:
sqlalchemy.exc.StatementError: (builtins.AttributeError) 'XRaySession' object has no attribute 'cursor'
I have these for my connections:
# Pooling
import sqlalchemy.pool as pool
#################### Engines ###################################################
def bpGetConnection():
engine_endpoint = f"mysql+pymysql://{os.environ['DB_USERNAME']}:{os.environ['DB_PASSWORD']}#{os.environ['DB_HOST']}:{str(os.environ['DB_PORT'])}/{os.environ['database']}"
engine = create_engine(engine_endpoint, echo_pool=True)
session = XRaySessionMaker(bind=engine, autoflush=True, autocommit=False)
db = session()
return db
bpPool = pool.StaticPool(bpGetConnection)
def companyGetConnection(database):
engine_endpoint = f"mysql+pymysql://{os.environ['DB_USERNAME']}:{os.environ['DB_PASSWORD']}#{os.environ['DB_HOST']}:{str(os.environ['DB_PORT'])}/{database}"
compEngine = create_engine(engine_endpoint, pool=bpPool)
session = XRaySessionMaker(bind=compEngine, autoflush=True, autocommit=False)
db = Session()
return db
#################### POOLING #############################################
def bpConnect():
conn = bpPool.connect()
return conn
def companyConnect(database):
conn = companyGetConnection(database)
return conn
#################################################################
They are called in this example:
from connections import companyConnect, bpConnect
from models import Company, Customers
def getCustomers(companyID):
db = bpConnect()
myQuery = db.query(Company).filter(Company.id == companyID).one()
compDB = companyConnect(myQuery.database)
customers = compDB.query(Customers).all()
return customers

I figured out how to do it with dynamic pools on a lambda:
class DBRegistry(object):
_db = {}
def get(self, url, **kwargs):
if url not in self._db:
engine = create_engine(url, **kwargs)
Session = XRaySessionMaker(bind=engine, autoflush=True, autocommit=False)
session = scoped_session(Session)
self._db[url] = session
return self._db[url]
compDB = DBRegistry()
def bpGetConnection():
engine_endpoint = f"mysql+pymysql://{os.environ['DB_USERNAME']}:{os.environ['DB_PASSWORD']}#{os.environ['DB_HOST']}:{str(os.environ['DB_PORT'])}/{os.environ['database']}?charset=utf8"
engine = create_engine(engine_endpoint)
session = XRaySessionMaker(bind=engine, autoflush=True, autocommit=False)
db = session()
return db
bpPool = pool.QueuePool(bpGetConnection, pool_size=500, timeout=11)
def bpConnect():
conn = bpPool.connect()
return conn
def companyConnect(database):
engine_endpoint = f"mysql+pymysql://{os.environ['DB_USERNAME']}:{os.environ['DB_PASSWORD']}#{os.environ['DB_HOST']}:{str(os.environ['DB_PORT'])}/{database}?charset=utf8"
conn = compDB.get(engine_endpoint, poolclass=QueuePool)
return conn
So basically it will use one pool for the constant connection needed on the main database and another pool which it will dynamically change the database it needs. When a connection to one of those company databases is needed, it will check if that pool already exists in the registry of pools. If the pool does not exist it will create one and register it.

Related

ORM Model class for Existing table in DB

ORM Model class for Existing table in DB from MicrosoftSqlServer, I tried but seems to be wrong this one is not a ORM, I don't know how to write ORM query can some one please help me.
Below is my code ca n some help me in converting this ORM based model class
from sqlalchemy import create_engine, select, MetaData, Table, and_
import json, boto3, os
from tests import test_dir
filename = os.path.join(test_dir, 'config.json')
with open(filename, 'r') as secret_data:
data = json.load(secret_data)
try:
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name=data['region_name']
)
get_secret_value_response = client.get_secret_value(
SecretId=data['secret_name']
)
response = json.loads(get_secret_value_response['SecretString'])
except Exception as e:
raise e
SERVER = response['server']
DATABASE = response['database']
DRIVER = 'SQL Server'
USERNAME = response['username']
PASSWORD = response['password'].strip()
DB_URL = f"mssql://{USERNAME}:{PASSWORD}#{SERVER}/{DATABASE}?driver={DRIVER}"
engine = create_engine(DB_URL)
metadata = MetaData(bind=None)
table = Table(
'Alias_Drug_List',
metadata,
autoload=True,
autoload_with=engine
)
newDrug = select([table.columns.Alias,table.columns.NDC]).where(and_(table.columns.NDC == 'New Drug Cycle'))
activeDrug = select([table.columns.Alias])
connection = engine.connect()
new_drug = connection.execute(newDrug).fetchall()
active_drug = connection.execute(activeDrug).fetchall()

How to mock db connection with Unittest for class with connection as context manager?

Here it is class with one of it's methods running when class is initializing:
class StatCollector:
def __init__(self, poll_stat) -> None:
self.polls = self.__get_polls()
def __get_polls(self) -> Dict[str, Poll]:
with pyodbc.connect(MSSQL_CONNECTION_PARAMS) as cnxn:
polls = dict()
cursor = cnxn.cursor()
query = self.poll_stat.poll_ids_query_getter()
cursor.execute(query, self.poll_stat.products_block)
for poll in map(Poll._make, cursor.fetchall()):
polls[poll.poll_id] = poll
return polls
I want to test other methods of this class and my first goal is to fill self.polls with initial values with no real connection to db and using __get_polls method. My try:
#patch("pyodbc.connect")
class testStatCollector(unittest.TestCase):
def test_initial_values_setted(self, mock_connect):
cursor = MagicMock(name="my_cursor")
cursor.fetchall.return_value = [("2", "А", "B")]
cnxn = MagicMock(name="my_cnxn_mfk")
cnxn.cursor.return_value = cursor
mock_connect.return_value.__enter__ = cnxn
self.test_class = PollsStatCollector(IVR)
self.assertEqual(
self.test_class.polls, {"2": Poll("2", "A", "B")}
)
self.assertIsInstance(self.test_class.period_start_time, datetime)
But self.polls are empty after execution. I got:
AssertionError: {} != {'2': Poll(poll_id='2', product='A', products_block='B')}
and I see in debug, that cnxn name = my_cnxn_mfk when __get_polls executing, but then cursor with default name = <MagicMock name='my_cnxn_mfk().cursor()' id='1883689785424'>.So i guess that i make mistake in this part cnxn.cursor.return_value = cursor, but i dont know how to fix it.
Mistake was here:
mock_connect.return_value.__enter__ = cnxn
Should be replaced with
mock_connect.return_value.__enter__.return_value = cnxn

Best practices when using a database and multiprocessing?

I'm trying to determine the correct way to establish a DB connection within a process.map() function. Do I establish the connection and close it within the function thats being run? Do I do this outside of the process.map()? Below is my some psuedo code thats close to what I currently have.
from multiprocessing import Pool, Manager, Process
import pyodbc
server = DB_SERVER
database = DATABASE
username = USERNAME
password = PASSWORD
def ref_worker(file):
conn = pyodbc.connect('DRIVER='+driver+';SERVER='+server+';PORT=1433;DATABASE='+database+';UID='+username+';PWD='+ password)
temp_data = pd.read_csv(file, dtype=str)
# logic here
cursor = conn.cursor()
sql_string = "insert into db (column) values(field)
cursor.execute(sql_string, temp_data)
cursor.commit()
cursor.close()
conn.close()
if __name__ == '__main__':
driver = ''
driver_names = [x for x in pyodbc.drivers() if x.endswith(' for SQL Server')]
if driver_names:
driver = driver_names[0]
if driver:
conn_str = 'DRIVER={}; ...'.format(driver)
else:
print('(No suitable driver found. Cannot connect.)')
file_list = pd.read_csv('list_of_files.csv', header=None, dtype=str)
pool = Pool(8) # Create a multiprocessing Pool
pool.map(ref_worker, file_list) # process data_inputs iterable with pool
pool.close()
pool.join()

Not able to iterate mongo object in python

I am trying to get the records (books), which are present the in Mongo DB collection "books". I am using pymongo and flask.
Below is my code. In the code, if I remove the query update line (query.update({'author': 'sachin'})), it works fine.
What is the problem while updating the query dict?
from pymongo import connection as pymongo_client
import urllib
def get_books(query, skip_val=0, limit=None):
conn, client = _connect()
if limit:
result = client.books.find(query).skip(skip_val).limit(limit)
else:
result = client.books.find(query)
conn.close()
return result
def _connect():
user = "root"
password = "password"
host = "172.17.1.14"
port = "27017"
db_name = "books"
auth_database = "admin"
if user and password and auth_database:
uri = "mongodb://%s:%s#%s:%s/%s" % (
user, urllib.quote_plus(password), host, port, auth_database)
else:
uri = "mongodb://%s:%s" % (host, port)
conn = pymongo_client.Connection(uri, j=True)
db = conn[db_name]
return conn, db
query = {'project_name': 'cricket'}
books = get_books(query)
query.update({'author': 'sachin'})
for book in books:
print book

cx_Oracle and context lib is this correct

I am curious to know if this is the correct way of using cx_Oracle with context lib and connection pooling using DBRCP.
import cx_Oracle
import threading
import time
def get_connection():
connection = cx_Oracle.connect(user='username', password='password', dsn='mydsn_name/service_name:pooled')
return connection
def myfunc():
with get_connection() as conn:
cursor = conn.cursor()
for _ in range(10):
cursor.execute("select * from mytable")
val = cursor.fetchone()
time.sleep(60)
print("Thread", threading.current_thread().name, "fetched sequence =", val)
results = []
for thread in range(0,10):
current_thread = threading.Thread(name = f'Thread {thread}', target = myfunc)
results.append(current_thread)
current_thread.start()
print('Started All Threads')
for thread in results:
thread.join()
print("All done!")
I am not sure If i am doing the right thing here .
And have no idea how to confirm that the connection is being returned to the connection pool.
And each thread is not opening a brand new connection to the database.
Although the doc's on cx_Oracle seem to indicate i am on the right path.
You'll get most benefit if you also use a cx_Oracle connect pool at the same time as DRCP. You need to set cclass with DRCP otherwise you will lose its benefits. You can then decide what level of session reuse (the 'purity') to use. Check the cx_Oracle tutorial. From solutions/connect_pool2.py:
pool = cx_Oracle.SessionPool("pythonhol", "welcome", "localhost/orclpdb:pooled",
min = 2, max = 5, increment = 1, threaded = True)
def Query():
con = pool.acquire(cclass="PYTHONHOL", purity=cx_Oracle.ATTR_PURITY_SELF)
#con = pool.acquire(cclass="PYTHONHOL", purity=cx_Oracle.ATTR_PURITY_NEW)
cur = con.cursor()
for i in range(4):
cur.execute("select myseq.nextval from dual")
seqval, = cur.fetchone()
There are V$ views like V$CPOOL_STATS you can query to check whether DRCP is being used. Links to some resources are in https://oracle.github.io/node-oracledb/doc/api.html#drcp

Categories