วิธีการใช้การเชื่อมต่อร่วมกันที่ดีที่สุดใน SQLAlchemy สำหรับการรวมธุรกรรมระดับ PgBouncer?


15

ใช้ SQLAlchemy เพื่อสืบค้นฐานข้อมูล PostgreSQL ที่อยู่เบื้องหลัง PgBouncer โดยใช้การรวมกำไรระดับธุรกรรม

รูปแบบที่ดีที่สุดที่จะใช้สำหรับการตั้งค่าประเภทนี้คืออะไร ฉันควรมีหนึ่งเครื่องยนต์ต่อกระบวนการใช้ConnectionPoolหรือฉันควรสร้างเครื่องยนต์ตามคำขอและใช้NullPoolสำหรับแต่ละเครื่องยนต์หรือไม่ มีรูปแบบที่แตกต่างกันโดยสิ้นเชิงที่ฉันควรใช้หรือไม่

ขอบคุณมาก ๆ! แจ้งให้เราทราบหากต้องการข้อมูลเพิ่มเติมและฉันจะอัปเดตโดยเร็ว

คำตอบ:


9

ด้วย PGBouncer คุณอาจต้องการติดกับ NullPool ในกรณีดังกล่าวคุณอาจแบ่งปัน Engine เดียวข้ามกระบวนการย่อยเนื่องจากไม่มีการเชื่อมต่อซ็อกเก็ตที่จะดำเนินการผ่านขอบเขตของกระบวนการย่อย แต่คุณไม่สามารถแบ่งปันสิ่งที่อ้างอิงถึงวัตถุการเชื่อมต่อเช่นเซสชันที่มีธุรกรรมที่ใช้งานอยู่ในขอบเขตนี้ แน่นอนว่าคุณไม่ต้องการทำ "เอ็นจินต่อคำขอ" เอ็นจิ้นเป็นวัตถุราคาแพงที่รวบรวมข้อมูลจำนวนมากเกี่ยวกับ URL ฐานข้อมูลเฉพาะในครั้งแรกที่เห็น


4

ตั้งชื่อแอปพลิเคชัน

หากคุณคาดว่าจะเรียกใช้กระบวนการจำนวนมากคุณจำเป็นต้องรู้ว่าพวกเขากำลังเชื่อมต่อจากที่ใด PGBouncer pg_stat_activityจะทำให้มองไม่เห็นนี้ แก้ปัญหาโดยการตั้งค่าapplication_nameข้อมูลที่คุณต้องการอย่างระมัดระวัง:

# Sets the application name for this connection in the form of
#   application-name:user@host
prog = os.path.basename(sys.argv[0]) or 'desjob'
username = pwd.getpwuid (os.getuid ()).pw_name
hostname = socket.gethostname().split(".")[0
args.setdefault('connect_args', {'application_name': "%s:%s@%s" %
    (prog, username, hostname)})
args.setdefault('isolation_level', "AUTOCOMMIT")
engine = create_engine(url, **args)

ชอบเซสชัน

ใช้เซสชันเนื่องจากคำขอจากวัตถุ Engine สามารถวางไข่และเชื่อมต่อกับหลาย ๆ การเชื่อมต่อได้ การเชื่อมต่อกับ Postgres นั้นไม่แพงมากนักด้วย PGBouncer มันก็น้อยกว่าเช่นกัน ฉันมักจะใช้NullPoolเพื่อให้การเชื่อมต่อเท่านั้นที่คุณจะเห็นใน Postgres คือการเชื่อมต่อที่ใช้งานจริง

from sqlalchemy.pool import Pool, NullPool
engine = create_engine(uri, poolclass=NullPool)

กำจัดธุรกรรมที่ไม่ได้ใช้งาน

หากความตั้งใจของคุณคือการใช้ PGBouncer ในการขยายขนาดคุณจำเป็นต้องหลีกเลี่ยงการทำธุรกรรมที่ค้างอยู่เปิด การทำเช่นนี้คุณจะต้องเปิดในautocommit นี่ไม่ใช่เรื่องง่ายด้วย SQLAlchemy ... มีสามตำแหน่งที่สิ่งที่เรียกว่า "autocommit" สามารถตั้งค่าได้:

psycopg2 อัตโนมัติ

conn = psycopg2.connect(uri)
conn.autocommit = True

สันนิษฐานว่าเป็นภัยที่ไม่ปลอดภัยเนื่องจาก SQLAlchemy ต้องการทราบว่าเกิดอะไรขึ้นภายใต้

การเติมข้อความอัตโนมัติในเซสชัน

Session = sessionmaker(bind=engine, autocommit=True)
session = Session()

สิ่งนี้ต้องใช้ความระมัดระวังและชัดเจนในการส่งมอบ:

session.begin()
session.execute(...)
session.rollback()

การเรียกใช้ฟังก์ชั่นและการส่งข้อยกเว้นนั้นยากมากเพราะ begin()และcommit()ไม่สามารถซ้อนกันได้:

def A():
  session.begin()
  ...
  session.rollback()

def B():
  session.begin()
  try:
      A() # error, already open

ในโหมดนี้ psycopg2 autocommitดูเหมือนจะเป็นFalse(ค่าเริ่มต้น)

AutoCommit ของโปรแกรม

การตั้งค่าโหมดการแยกเครื่องยนต์เป็น"AUTOCOMMIT"เมื่อสร้างเครื่องยนต์สร้างลักษณะการทำงานเริ่มต้นใหม่ที่อาจไม่ต้องการการเปลี่ยนแปลงรหัสที่มีอยู่

engine = create_engine(uri, isolation_level="AUTOCOMMIT")

ในโหมดนี้ psycopg2 autocommitดูเหมือนจะเป็นTrue

ปัญหาสำคัญที่นี่คือวิธีเดียวที่จะรับประกันได้ว่าการรวมกลุ่มของโค้ดในธุรกรรมคือการเผยแพร่ข้อความด้วยตนเอง:

session.execute("BEGIN")
#...
session.execute("COMMIT")
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.