Skip to content
quarry Artifact [c0813cf5a5]
Login

Artifact [c0813cf5a5]

Artifact c0813cf5a5588ea43160c20698edf3f13c6ba6f4f3646a7804b58d3f0dd21e60:


package index

import (
	"path/filepath"
	"strings"
	"testing"
)

// A bulk rebuild recreates its dropped indexes, and the writer that
// does it keeps temp b-trees on disk while readers keep theirs in
// memory: on netbsd-src the in-memory sort ran SQLite out of memory and
// took the forge down (#357).
func TestResumeRebuildsIndexesWithTheSortOnDisk(t *testing.T) {
	ix := openIndex(t)
	if err := ix.SuspendDerivedMaintenance(); err != nil {
		t.Fatal(err)
	}
	if err := ix.ResumeDerivedMaintenance(); err != nil {
		t.Fatal(err)
	}
	for _, name := range bulkDroppable {
		var n int
		if err := ix.w.QueryRow(`SELECT count(*) FROM sqlite_master WHERE type = 'index' AND name = ?`, name).Scan(&n); err != nil {
			t.Fatal(err)
		}
		if n != 1 {
			t.Errorf("FAIL: index %s not rebuilt", name)
		}
	}
	var w, r int
	if err := ix.w.QueryRow(`PRAGMA temp_store`).Scan(&w); err != nil {
		t.Fatal(err)
	}
	if err := ix.db.QueryRow(`PRAGMA temp_store`).Scan(&r); err != nil {
		t.Fatal(err)
	}
	if w != 1 || r != 2 {
		t.Errorf("FAIL: temp_store writer %d reader %d, want 1 (file) and 2 (memory)", w, r)
	}
}

// Every index connection may use connMaxMemory, not the driver's 256MB.
func TestIndexConnectionsCarryTheMemoryCeiling(t *testing.T) {
	ix := openIndex(t)
	for _, q := range []struct {
		name string
		lim  func() (int64, error)
	}{
		{"writer", func() (n int64, err error) { err = ix.w.QueryRow(`PRAGMA soft_heap_limit`).Scan(&n); return }},
		{"reader", func() (n int64, err error) { err = ix.db.QueryRow(`PRAGMA soft_heap_limit`).Scan(&n); return }},
	} {
		n, err := q.lim()
		if err != nil {
			t.Fatal(err)
		}
		if n < 512<<20 {
			t.Errorf("FAIL: %s soft heap limit %d, want near %d", q.name, n, connMaxMemory)
		}
	}
}

// An index that runs SQLite out of memory while opening is an error for
// that store, not a panic: at startup every store opens at once, and a
// panic there ended the forge on every restart (#357).
func TestAnIndexThatRunsOutOfMemoryFailsToOpen(t *testing.T) {
	old := connMaxMemory
	connMaxMemory = 1 << 16 // one wasm page: far too little to apply the schema
	defer func() { connMaxMemory = old }()
	ix, err := Open(filepath.Join(t.TempDir(), "index.db"))
	if err == nil {
		ix.Close()
		t.Fatal("FAIL: an index with no memory to work in opened")
	}
	if !strings.Contains(err.Error(), "out of memory") {
		t.Errorf("FAIL: want the out-of-memory error, got %v", err)
	}
}

// While the per-path file index is dropped, a file's last change
// answers empty at once instead of scanning every file change.
func TestLastChangesWaitForTheBulkRebuild(t *testing.T) {
	ix := openIndex(t)
	if err := ix.SuspendDerivedMaintenance(); err != nil {
		t.Fatal(err)
	}
	got, err := ix.LastChanges([]string{"a.txt"})
	if err != nil || len(got) != 0 {
		t.Fatalf("FAIL: LastChanges during bulk = %v, %v", got, err)
	}
	if _, ok, err := ix.LastChange("a.txt", ""); err != nil || ok {
		t.Fatalf("FAIL: LastChange during bulk = %v, %v", ok, err)
	}
}