import sqlite3, os, sys, time, random

# DBはコンテナ内のパスに作る（重要: -v でマウントした先に作らない）
WORK = "/tmp/bench-data"
os.makedirs(WORK, exist_ok=True)

def fresh(path, journal="DELETE", synchronous=None, populate=0):
    """毎回まっさらなDBを作って返す"""
    for ext in ("", "-wal", "-shm", "-journal"):
        try:
            os.remove(path + ext)
        except FileNotFoundError:
            pass
    con = sqlite3.connect(path)
    con.execute(f"PRAGMA journal_mode={journal}")
    if synchronous:
        con.execute(f"PRAGMA synchronous={synchronous}")
    con.execute("CREATE TABLE kv (k TEXT PRIMARY KEY, v TEXT)")
    if populate:
        with con:
            con.executemany("INSERT INTO kv VALUES (?, ?)",
                            ((f"k{i}", "x" * 100) for i in range(populate)))
    return con

print(f"SQLite {sqlite3.sqlite_version} / Python {sys.version.split()[0]} / {sys.platform}")
print()

# ---- 実験1: 読み取り速度（ファイルDB vs :memory:） ----
def bench_reads(con, n=200_000):
    random.seed(42)
    keys = [f"k{random.randrange(100_000)}" for _ in range(n)]
    cur = con.cursor()
    t0 = time.perf_counter()
    for k in keys:
        cur.execute("SELECT v FROM kv WHERE k = ?", (k,))
        cur.fetchone()
    return n / (time.perf_counter() - t0)

fpath = os.path.join(WORK, "read.db")
fcon = fresh(fpath, "WAL", populate=100_000)
mcon = sqlite3.connect(":memory:")
mcon.execute("CREATE TABLE kv (k TEXT PRIMARY KEY, v TEXT)")
with mcon:
    mcon.executemany("INSERT INTO kv VALUES (?, ?)",
                     ((f"k{i}", "x" * 100) for i in range(100_000)))
print(f"読み取り ファイルDB : {bench_reads(fcon):>9,.0f} クエリ/秒")
print(f"読み取り :memory:  : {bench_reads(mcon):>9,.0f} クエリ/秒")
mcon.close()
print()

# ---- 実験2: コミット速度（ジャーナルモード・同期設定・まとめ書きの効き方） ----
def bench_commits(journal, synchronous, batch, n=500):
    con = fresh(os.path.join(WORK, "bench.db"), journal, synchronous)
    t0 = time.perf_counter()
    if batch:  # n件を1つのトランザクションで
        with con:
            for i in range(n):
                con.execute("INSERT INTO kv VALUES (?, ?)", (f"k{i}", "x" * 100))
    else:      # 1件ずつコミット
        for i in range(n):
            with con:
                con.execute("INSERT INTO kv VALUES (?, ?)", (f"k{i}", "x" * 100))
    rate = n / (time.perf_counter() - t0)
    con.close()
    return rate

for label, args in [
    ("1件ずつ（既定: ロールバックジャーナル）", ("DELETE", "FULL", False)),
    ("1件ずつ（WALモード）　　　　　　　　　", ("WAL", "FULL", False)),
    ("1件ずつ（WAL + NORMAL）　　　　　　　", ("WAL", "NORMAL", False)),
    ("500件一括（既定のまま）　　　　　　　　", ("DELETE", "FULL", True)),
    ("500件一括（WAL + NORMAL）　　　　　　", ("WAL", "NORMAL", True)),
]:
    print(f"コミット {label} {bench_commits(*args):>11,.0f} insert/秒")
print()

# ---- 実験3: ロック（書き手2人 / 読み手がいる最中のコミット） ----
lpath = os.path.join(WORK, "lock.db")
for journal in ("DELETE", "WAL"):
    con = fresh(lpath, journal)
    con.execute("INSERT INTO kv VALUES ('a', '1')")
    con.commit()
    con.close()

    w1 = sqlite3.connect(lpath, timeout=0)
    w2 = sqlite3.connect(lpath, timeout=0)
    w1.execute("BEGIN IMMEDIATE")
    w1.execute("UPDATE kv SET v = 'w1' WHERE k = 'a'")
    try:
        w2.execute("BEGIN IMMEDIATE")
        print(f"ロック[{journal}] 書き手2人: 2人目も成功（想定外）")
    except sqlite3.OperationalError as e:
        print(f"ロック[{journal}] 書き手2人: 2人目 → {e}")
    w1.rollback(); w1.close(); w2.close()

    r = sqlite3.connect(lpath, timeout=0)
    w = sqlite3.connect(lpath, timeout=0)
    r.execute("BEGIN")
    r.execute("SELECT * FROM kv").fetchall()
    try:
        w.execute("BEGIN IMMEDIATE")
        w.execute("UPDATE kv SET v = 'w' WHERE k = 'a'")
        w.commit()
        print(f"ロック[{journal}] 読み手がいる最中のコミット: 成功")
    except sqlite3.OperationalError as e:
        print(f"ロック[{journal}] 読み手がいる最中のコミット: {e}")
    r.close(); w.close()
print()

# ---- 実験4: 接続の開き直しコスト（PHPの毎リクエスト相当） ----
N = 2000
t0 = time.perf_counter()
for i in range(N):
    c = sqlite3.connect(fpath)
    c.execute("SELECT v FROM kv WHERE k = ?", (f"k{i % 100000}",)).fetchone()
    c.close()
open_each = (time.perf_counter() - t0) / N

c = sqlite3.connect(fpath)
t0 = time.perf_counter()
for i in range(N):
    c.execute("SELECT v FROM kv WHERE k = ?", (f"k{i % 100000}",)).fetchone()
reuse = (time.perf_counter() - t0) / N
c.close()
fcon.close()
print(f"接続 毎回開閉: {open_each*1e6:6.1f} マイクロ秒/回   使い回し: {reuse*1e6:5.1f} マイクロ秒/回")
