ncruces / go-sqlite3

Go bindings to SQLite using wazero
https://pkg.go.dev/github.com/ncruces/go-sqlite3
MIT License
499 stars 16 forks source link

wazero v1.7.0-pre.1. #60

Closed ncruces closed 7 months ago

ncruces commented 8 months ago

This fails very consistently after updating wazero to the optimizing compiler.

On:

goos: linux
goarch: amd64
cpu: AMD EPYC 7B12
version: go1.22.0 linux/amd64
=== RUN   TestMemory
panic: wasm error: out of bounds memory access
wasm stack trace:
    .sqlite3FindTable(i32,i32,i32) i32
    .sqlite3LocateTable(i32,i32,i32,i32) i32
    .selectExpander(i32,i32) i32
    .sqlite3WalkSelect(i32,i32) i32
    .sqlite3SelectPrep(i32,i32,i32)
    .sqlite3Select(i32,i32,i32) i32
    .sqlite3RunParser(i32,i32) i32
    .sqlite3Prepare(i32,i32,i32,i32,i32,i32,i32) i32
    .sqlite3LockAndPrepare(i32,i32,i32,i32,i32,i32,i32) i32
    .sqlite3_prepare_v3(i32,i32,i32,i32,i32,i32) i32
    .sqlite3_vtab_config_go(i32,i32,i32) i32

goroutine 8700 [running]:
github.com/ncruces/go-sqlite3.(*sqlite).call(0xc0000b4708, {0x728b5c, 0x12?}, {0xc00047de18?, 0xc00047de08?, 0x68614c?})
    /home/runner/work/go-sqlite3/go-sqlite3/sqlite.go:181 +0x1ad
github.com/ncruces/go-sqlite3.(*Conn).PrepareFlags(0xc0003e4000, {0x72ca56, 0x1a}, 0x3c57b8?)
    /home/runner/work/go-sqlite3/go-sqlite3/conn.go:186 +0x1be
github.com/ncruces/go-sqlite3.(*Conn).Prepare(...)
    /home/runner/work/go-sqlite3/go-sqlite3/conn.go:167
github.com/ncruces/go-sqlite3/tests/parallel.testParallel.func2()
    /home/runner/work/go-sqlite3/go-sqlite3/tests/parallel/parallel_test.go:146 +0xc7
golang.org/x/sync/errgroup.(*Group).Go.func1()
    /home/runner/go/pkg/mod/golang.org/x/sync@v0.6.0/errgroup/errgroup.go:78 +0x56
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 5002
    /home/runner/go/pkg/mod/golang.org/x/sync@v0.6.0/errgroup/errgroup.go:75 +0x96
FAIL    github.com/ncruces/go-sqlite3/tests/parallel    11.322s

On:

goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
version: go1.22.0 darwin/amd64
=== RUN   TestMemory
panic: wasm error: out of bounds memory access
wasm stack trace:
        .sqlite3FindTable(i32,i32,i32) i32
        .sqlite3LocateTable(i32,i32,i32,i32) i32
        .selectExpander(i32,i32) i32
        .sqlite3WalkSelect(i32,i32) i32
        .sqlite3SelectPrep(i32,i32,i32)
        .sqlite3Select(i32,i32,i32) i32
        .sqlite3RunParser(i32,i32) i32
        .sqlite3Prepare(i32,i32,i32,i32,i32,i32,i32) i32
        .sqlite3LockAndPrepare(i32,i32,i32,i32,i32,i32,i32) i32
        .sqlite3_prepare_v3(i32,i32,i32,i32,i32,i32) i32
        env.go_cur_rowid(i32,i32) i32

goroutine 6490 [running]:
github.com/ncruces/go-sqlite3.(*sqlite).call(0xc0000de008, {0x942c1a5, 0x12?}, {0xc00076de18?, 0xc00076de08?, 0x941b3ac?})
        /Users/cruces/Documents/Personal/go-sqlite3/sqlite.go:181 +0x1ad
github.com/ncruces/go-sqlite3.(*Conn).PrepareFlags(0xc000862000, {0x942fff1, 0x1a}, 0x0?)
        /Users/cruces/Documents/Personal/go-sqlite3/conn.go:186 +0x1be
github.com/ncruces/go-sqlite3.(*Conn).Prepare(...)
        /Users/cruces/Documents/Personal/go-sqlite3/conn.go:167
github.com/ncruces/go-sqlite3/tests/parallel.testParallel.func2()
        /Users/cruces/Documents/Personal/go-sqlite3/tests/parallel/parallel_test.go:146 +0xc7
golang.org/x/sync/errgroup.(*Group).Go.func1()
        /Users/cruces/go/pkg/mod/golang.org/x/sync@v0.6.0/errgroup/errgroup.go:78 +0x56
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 5053
        /Users/cruces/go/pkg/mod/golang.org/x/sync@v0.6.0/errgroup/errgroup.go:75 +0x96
exit status 2
FAIL    github.com/ncruces/go-sqlite3/tests/parallel    4.399s

On:

goos: windows
goarch: amd64
cpu: Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz
version: go1.22.0 windows/amd64
=== RUN   TestMemory
panic: wasm error: out of bounds memory access
wasm stack trace:
        .sqlite3FindTable(i32,i32,i32) i32
        .sqlite3LocateTable(i32,i32,i32,i32) i32
        .selectExpander(i32,i32) i32
        .sqlite3WalkSelect(i32,i32) i32
        .sqlite3SelectPrep(i32,i32,i32)
        .sqlite3Select(i32,i32,i32) i32
        .sqlite3RunParser(i32,i32) i32
        .sqlite3Prepare(i32,i32,i32,i32,i32,i32,i32) i32
        .sqlite3LockAndPrepare(i32,i32,i32,i32,i32,i32,i32) i32
        .sqlite3_prepare_v3(i32,i32,i32,i32,i32,i32) i32
        env.go_cur_rowid(i32,i32) i32

goroutine 5365 [running]:
github.com/ncruces/go-sqlite3.(*sqlite).call(0xc000005508, {0xfa2e71, 0x12?}, {0xc0003dde18?, 0xc0003dde08?, 0xeffeac?})
        D:/Development/GitHub/go-sqlite3/sqlite.go:181 +0x1ad
github.com/ncruces/go-sqlite3.(*Conn).PrepareFlags(0xc0003c81b0, {0xfa80ff, 0x1a}, 0x0?)
        D:/Development/GitHub/go-sqlite3/conn.go:186 +0x1be
github.com/ncruces/go-sqlite3.(*Conn).Prepare(...)
        D:/Development/GitHub/go-sqlite3/conn.go:167
github.com/ncruces/go-sqlite3/tests/parallel.testParallel.func2()
        D:/Development/GitHub/go-sqlite3/tests/parallel/parallel_test.go:146 +0xc7
golang.org/x/sync/errgroup.(*Group).Go.func1()
        D:/Apps/Go/work/pkg/mod/golang.org/x/sync@v0.6.0/errgroup/errgroup.go:78 +0x56
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 5031
        D:/Apps/Go/work/pkg/mod/golang.org/x/sync@v0.6.0/errgroup/errgroup.go:75 +0x96
exit status 2
FAIL    github.com/ncruces/go-sqlite3/tests/parallel    12.457s
ncruces commented 8 months ago

The test passes on arm64, both on an M1 macOS, and QEMU Linux: https://github.com/ncruces/go-sqlite3/actions/runs/7957530580

ncruces commented 8 months ago

A few random notes:

Trace before sqlite3_prepare_v3 makes no sense. go-sqlite3.(*Conn).PrepareFlags calls sqlite3_prepare_v3 directly, and the test doesn't involve virtual tables (sqlite3_vtab_config_go is virtual table API, env.go_cur_rowid is a virtual table callback). These are different between macOS and Linux, but consistent (always the same on macOS, always the same on Linux, even across different machines).

I don't see anything specific about this code path. All I can say is involves goroutines (not WASM threads), and callbacks. Each goroutine shares the runtime, compiled module and host module, but has a different instantiated module. The host callbacks contend on some locks that protect host memory. The instantiated modules and their memories are not shared, not accessed across goroutines.

Here is the failing test: https://github.com/ncruces/go-sqlite3/blob/e02c5b5db04fad6a839cb6e3f81141347dd18f1a/tests/parallel/parallel_test.go#L35-L46

The bulk of it is testParallel. This will run 5000 goroutines (with a limit of 6 concurrent goroutines): 12.5% writers and 87.5% readers. The database is held in memory, using the virtual file-system mechanism of SQLite.

ncruces commented 8 months ago

https://github.com/tetratelabs/wazero/commit/8caae04aeae2089a327d27f5a87827f5f35ff09e seems to fix this, but has some incompatible API changes, so this needs to wait for a release.

Even then I may not change the default compiler: users can do so by editing sqlite3.RuntimeConfig. Once released, I may add some explicit tests to ensure it keeps working, though.

ncruces commented 8 months ago

Everything works with wazevo as the default compiler. Ready for release.