SQLite / دليل حلول تحريري
تشخيص الانشغال والقفل واللقطة القديمة
صنّف نتيجة SQLite وحالة المعاملة قبل الانتظار أو الإعادة أو تغيير إعداد السجل.
قاعدة معرفة للقراءة فقط · بلا تعليقات عامة
تعرّف على الأعراض
قد يعرض التطبيق database is locked أو SQLITE_BUSY أو SQLITE_LOCKED أو الرمز الممتد SQLITE_BUSY_SNAPSHOT. لا يثبت تشابه النص تطابق السبب أو التنظيف اللازم.
قد يفشل الطلب فوراً رغم مهلة تبدو معقولة، أو تنمو WAL مع قارئ طويل. احفظ الرمز الدقيق وحد العملية بدلاً من الاكتفاء بعبارة «القاعدة مقفلة».
افهم الأسباب المحتملة
- قد يملك اتصال آخر دور الكاتب، أو يحتفظ مؤشر مفتوح بالموارد، أو تصبح لقطة قراءة قديمة غير قابلة للتصعيد بعد تثبيت آخر.
- قد يغير افتراضي المشغل أو الذاكرة المشتركة أو نظام الملفات أو معاملة غير منتهية السياق. لا تلغي WAL قاعدة الكاتب الواحد ولا تزيل كل انشغال.
سجّل سياق الاتصال والمعاملة
احفظ إصدار المكتبة وبناءها ونمط السجل وإعداد معاملات المضيف والرمز الممتد ومفتاح العملية ومالك كل معاملة. أبق المسار الحقيقي وقيم SQL في تشخيص مخول. لا تحذف الملفات المرافقة أو تعيد خدمات أو تفتح قاعدة مشروع أثناء هذا الدليل. حدد هل فشل بدء كتابة أم تصعيد قراءة أم COMMIT؛ فذلك يغير معنى الفشل وخيارات التعامل معه.
افصل الانتظار من اللقطة القديمة
قد تنتظر مهلة الانشغال تنازعاً عابراً، لكنها لا تجعل لقطة WAL قديمة أساساً صالحاً للكتابة. يحتاج BUSY_SNAPSHOT عادة إلى التخلي عن اللقطة وإعادة المعاملة المؤهلة كاملة بقراءات جديدة. وقد تترك COMMIT المنشغلة عملاً نشطاً بخيارات مختلفة. افحص SQLITE_LOCKED وفق قواعد الاتصال والمؤشر ولا تجمع كل الأرقام في إعادة عامة واحدة.
استخدم جدولاً خاصاً محدوداً
ينشئ البرنامج ملفه المؤقت الخاص ويحجز معاملة كتابة، ويجعل انتظار الاتصال الثاني صفراً. يرفض الإدراج بـSQLITE_BUSY ثم ينجح بعد تراجع الأول. هذا تنسيق متتابع وليس اختبار حمل أو محاولة سباق WAL-reset. تغلق الاتصالات قبل إزالة المجلد، ويظل الصف الأصلي مع الإدراج اللاحق ليصبح العدد اثنين دون المساس ببيانات فعلية.
import sqlite3
from pathlib import Path
from tempfile import TemporaryDirectory
from contextlib import closing
with TemporaryDirectory(prefix="sqlite-guide-busy-") as temporary:
path = Path(temporary) / "training.db"
with closing(sqlite3.connect(path, isolation_level=None, timeout=0)) as writer, \
closing(sqlite3.connect(path, isolation_level=None, timeout=0)) as contender:
writer.executescript("CREATE TABLE item(id INTEGER PRIMARY KEY); INSERT INTO item VALUES(1);")
writer.execute("BEGIN IMMEDIATE")
try:
contender.execute("INSERT INTO item VALUES(2)")
except sqlite3.OperationalError as error:
assert error.sqlite_errorcode == sqlite3.SQLITE_BUSY
print("blocked", error.sqlite_errorname)
else:
raise AssertionError("expected contention")
finally:
writer.execute("ROLLBACK")
contender.execute("INSERT INTO item VALUES(2)")
print("rows", contender.execute("SELECT COUNT(*) FROM item").fetchone()[0])
blocked SQLITE_BUSY
rows 2
يحجب الكاتب المحتفظ به الإدراج، ويسمح تحريره الصريح بإدراج لاحق واحد.
أصلح الملكية واختر إعادة محدودة
قصّر المعاملات المملوكة وأنهِ القراء سريعاً وحدد قبول الكتابة وفق التطبيق. قد تطلب BEGIN IMMEDIATE الدور مبكراً فتتجنب بعض مفاجآت التصعيد، لكنها ليست علاجاً عاماً. ضع حدوداً للإعادة واحفظ هوية الطلب وصنّف النتيجة المجهولة. راجع تصحيحات WAL لبناء النشر؛ فبيئة الدرس3.45.1 هوية تحقق تاريخية لا توصية تصحيح إنتاجي.
تحقق من نجاح الإصلاح
تحقق من التداخل في قاعدة خاصة ثم النجاح والتراجع وتحرير القارئ وحد الإعادة وعدم تكرار أثر العمل. افحص حالة المعاملة فعلاً بعد كل فشل، ولا تعتبر المهلة الأطول أو تجربة أهدأ برهان إصلاح.
امنع تكرار المشكلة
- وثق المالك وميزانية مدة المعاملة وافتراض المشغل والإلغاء والسجل والنسخ والإصدار المصحح المدعوم. احفظ الرمز الممتد المفيد دون كشف خاص، واجعل التعامل مع المرافق جزءاً من دورة موارد واسترداد معتمدة.