Slide 43 de 53

A pegadinha do banco em memória

Um tipo de linha
type User { id: Int, name: Str }

query findUser(name: Str): User[dyn] {
	SELECT id, name FROM users WHERE name = {name}
}

proc lookUp(db: hive.sql.SqlConnection, name: Str): Str {
	if using db run findUser(name) is Result.Ok(rows) {
		if rows bounds 0 {
			return rows[0].name
		}
	}
	return "?"
}

proc main(): void {
	// Um ":memory:" simples daria a cada uma destas oito conexões um banco
	// privado e vazio, só dela. O cache compartilhado é o que faz todas
	// elas serem o mesmo banco.
	opened := hive.sql.pool(hive.sql.DatabaseDriver.SQLite(), "file::memory:?cache=shared", 8, 1)
	if opened is Result.Ok(db) {
		using db run raw "CREATE TABLE users (id INTEGER, name TEXT)"
		using db run raw "INSERT INTO users (id, name) VALUES (1, 'ada'), (2, 'grace')"

		echo await [lookUp(db, "ada"), lookUp(db, "grace")]
		hive.sql.close(db)
	}
}
Um banco SQLite :memory: simples pertence à conexão, não ao processo. Cada conexão recebe um banco privado e vazio que desaparece quando ela fecha — e combinado com um pool, isso tem uma aresta afiada. Uma query por vez funciona bem. Oito de uma vez não: o pool abre mais conexões, cada uma cai em um banco próprio, e as queries que foram para as novas voltam com "no such table". Um programa que passa em todo teste em thread única começa a falhar no instante em que as requisições se sobrepõem, que é exatamente o que acontece atrás de um servidor HTTP, já que ele roda cada requisição em uma thread própria.Existem duas saídas, e só uma delas é uma resposta de verdade. pool(…, 1, 1) segura o pool em uma única conexão, o que funciona mas serializa toda query do programa por ela. "file::memory:?cache=shared" é a que se deve usar: as conexões compartilham um único banco em memória, então o pool pode ser tão largo quanto o trabalho for. Nada disso é regra do Hive — é do SQLite — mas é o que vale saber sobre um banco em memória antes que a produção lhe ensine.