DbApp

io.eezo.db.DbApp
trait DbApp extends Dispatch, DbInit

The database layer's entry trait: the one an application on eezo-db alone extends.

object Main extends DbApp {
 override def schema: Schema = AppSchema
 override def boot(): Unit   = transact { /* a job over rows */ }
}

The layer is declarative. The application names its schema and says what boot does; the framework builds the Database from DbInit's overrides, installs it around the program, and closes it after. main is inherited from Dispatch: sbt run runs the program once with a database installed, and sbt "run status|sync|freeze|migrate|reset|drop|dump|ddl" are the schema commands (design/cli.md §4). dev and routes are the http layer's and do not exist here: eezoDev on a database only application prints the unknown command line on every save, and a rerun on save loop for a job is sbt's own ~run.

A Database is installed only around what needs one. ddl, dump, freeze and help never touch it, so they work with the database down; the program and the other commands get one for their whole duration. Installing never waits on the network: the pool starts empty (initializationFailTimeout negative), so Database.connect returns at once and the pool fills in the background. It is deliberately not eager, as Phoenix is not: with Postgres down, dev keeps serving every page that needs no database, and sbt run fails on the first query that needs Postgres, after the acquire timeout, rather than refusing to start.

Attributes

Source
DbApp.scala
Graph
Supertypes
trait DbInit
trait Dispatch
class Object
trait Matchable
class Any
Known subtypes
trait EezoApp

Members list

Value members

Abstract methods

def boot(): Unit

The program: what sbt run does, once, with a Database installed. Abstract, because a database only application is its program. The umbrella supplies the http layer's default, which is to serve.

The program: what sbt run does, once, with a Database installed. Abstract, because a database only application is its program. The umbrella supplies the http layer's default, which is to serve.

Attributes

Source
DbApp.scala
def schema: Schema

The application's schema, object AppSchema extends Schema named here. Abstract: a database layer is its schema, and an application without one is a mistake.

The application's schema, object AppSchema extends Schema named here. Abstract: a database layer is its schema, and an application without one is a mistake.

Attributes

Source
DbApp.scala

Concrete methods

override protected def commands: PartialFunction[List[String], Int]

The commands this application answers, as first argument :: rest. Nil is program and is answered here, once, so no layer owns it. Each layer writes own orElse super.commands for its own commands, so the later mixin's arms are consulted first.

The commands this application answers, as first argument :: rest. Nil is program and is answered here, once, so no layer owns it. Each layer writes own orElse super.commands for its own commands, so the later mixin's arms are consulted first.

Attributes

Definition Classes
Source
DbApp.scala
def databaseSchema: String

The Postgres schema the drift commands introspect. search_path is set per connection in databaseInit; this is the same name, told to the side that reads catalogs.

The Postgres schema the drift commands introspect. search_path is set per connection in databaseInit; this is the same name, told to the side that reads catalogs.

Attributes

Source
DbApp.scala
override protected def notes: List[String]

What help says after the command table and before its footer: the caveats that belong to no single row. Each layer writes own ++ super.notes, and they are kept apart from usage so a stacked application prints one table and then every layer's notes, rather than one layer's notes in the middle of the other's commands.

What help says after the command table and before its footer: the caveats that belong to no single row. Each layer writes own ++ super.notes, and they are kept apart from usage so a stacked application prints one table and then every layer's notes, rather than one layer's notes in the middle of the other's commands.

Attributes

Definition Classes
Source
DbApp.scala
protected def program(): Unit

What sbt run does on this layer: boot with a Database installed around it. Concrete here and in the http layer both, so a trait that stacks the two inherits two and must say which one it means; the umbrella says this one. No override here, deliberately: see Dispatch.program.

What sbt run does on this layer: boot with a Database installed around it. Concrete here and in the http layer both, so a trait that stacks the two inherits two and must say which one it means; the umbrella says this one. No override here, deliberately: see Dispatch.program.

Attributes

Source
DbApp.scala
override protected def usage: List[Usage]

One row per command, for help. Each layer writes own ++ super.usage, and names only the spelling and the description: the columns are help's to lay out, once, over every layer's rows together, so no layer has to guess how wide the others' spellings are.

One row per command, for help. Each layer writes own ++ super.usage, and names only the spelling and the description: the columns are help's to lay out, once, over every layer's rows together, so no layer has to guess how wide the others' spellings are.

Attributes

Definition Classes
Source
DbApp.scala
final protected def withDatabase[A](f: => A): A

The lifecycle, scoped to one command: built, installed, closed. Final, and protected rather than private so the umbrella can wrap the dev server in it.

The lifecycle, scoped to one command: built, installed, closed. Final, and protected rather than private so the umbrella can wrap the dev server in it.

Attributes

Source
DbApp.scala

Inherited methods

Short on purpose. detached borrows a second connection while the caller still holds its first, so one thread can hold two, and a full pool can deadlock on itself; the timeout is the only thing that turns that into an error. HikariCP's own 30 seconds reads as a hang to whoever is waiting on the request, and a request that cannot get a connection in 5 seconds is better answered with a 500 than left open.

Short on purpose. detached borrows a second connection while the caller still holds its first, so one thread can hold two, and a full pool can deadlock on itself; the timeout is the only thing that turns that into an error. HikariCP's own 30 seconds reads as a hang to whoever is waiting on the request, and a request that cannot get a connection in 5 seconds is better answered with a 500 than left open.

Attributes

Inherited from:
DbInit
Source
DbInit.scala
def databaseInit: Connection => Unit

Run on every connection the pool creates, not once on a borrowed one (DESIGN §8.7).

Run on every connection the pool creates, not once on a borrowed one (DESIGN §8.7).

This is where search_path, application_name and statement timeouts belong. Without it an application that works in a non-public schema silently splits in two: its own connections see one schema and eezo's see another, which shows up as a migration that appears not to have run.

Attributes

Inherited from:
DbInit
Source
DbInit.scala
def databasePassword: String

Attributes

Inherited from:
DbInit
Source
DbInit.scala
def databasePoolSize: Int

Small on purpose. HikariCP's sizing rule is connections = cores * 2 + spindles, because Postgres does its work on CPU and disk, and connections beyond what those can serve only queue inside the database instead of in the pool, where queueing is cheaper. Virtual threads make requests cheap, not connections: thousands of requests waiting briefly on a small saturated pool is the intended shape, not a sign the pool is too small.

Small on purpose. HikariCP's sizing rule is connections = cores * 2 + spindles, because Postgres does its work on CPU and disk, and connections beyond what those can serve only queue inside the database instead of in the pool, where queueing is cheaper. Virtual threads make requests cheap, not connections: thousands of requests waiting briefly on a small saturated pool is the intended shape, not a sign the pool is too small.

Attributes

Inherited from:
DbInit
Source
DbInit.scala
def databaseUrl: String

Overridable, so a test or a second environment can point elsewhere without touching boot.

Overridable, so a test or a second environment can point elsewhere without touching boot.

Attributes

Inherited from:
DbInit
Source
DbInit.scala
def databaseUser: String

Attributes

Inherited from:
DbInit
Source
DbInit.scala
final def main(args: Array[String]): Unit

Attributes

Inherited from:
Dispatch
Source
Dispatch.scala