• Stars
    star
    215
  • Rank 183,925 (Top 4 %)
  • Language
    Scala
  • License
    Apache License 2.0
  • Created almost 15 years ago
  • Updated about 14 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An agreeable way to talk to your database.

Querulous

An agreeable way to talk to your database.

License

Copyright 2010 Twitter, Inc. See included LICENSE file.

Features

  • Handles all the JDBC bullshit so you don't have to: type casting for primitives and collections, exception handling and transactions, and so forth;
  • Fault tolerant: configurable strategies such as timeouts, mark-dead thresholds, and retries;
  • Designed for operability: rich statistics about your database usage and extensive debug logging;
  • Minimalist: minimal code, minimal assumptions, minimal dependencies. You write highly-tuned SQL and we get out of the way;
  • Highly modular, highly configurable.

The Github source repository is {here}[http://github.com/nkallen/querulous/]. Patches and contributions are
welcome.

Understanding the Implementation

Querulous is made out of three components: QueryEvaluators, Queries, and Databases.

  • QueryEvaluators are a convenient procedural interface for executing queries.
  • Queries are objects representing a SELECT/UPDATE/INSERT/DELETE SQL Query. They are responsible for most type-casting, timeouts, and so forth. You will rarely interact with Queries directly.
  • Databases reserve and release connections an actual database.

Each of these three kinds of objects implement an interface. Enhanced functionality is meant to be "layered-on" by wrapping decorators around these objects that implement the enhanced functionality and delegate the primitive functionality.

Each of the three components are meant to be instantiated with their corresponding factories (e.g., QueryEvaluatorFactory, DatabaseFactory, etc.). The system is made configurable by constructing factories that manufacture the Decorators you're interested in. For example,

val queryFactory = new DebuggingQueryFactory(new TimingOutQueryFactory(new SqlQueryFactory))
val query = queryFactory(...) // this query will have debugging information and timeouts!

Usage

Basic Usage

Create a QueryEvaluator by connecting to a database host with a username and password:

import com.twitter.querulous.evaluator.QueryEvaluator
val queryEvaluator = QueryEvaluator("host", "username", "password")

Run a query or two:

val users = queryEvaluator.select("SELECT * FROM users WHERE id IN (?) OR name = ?", List(1,2,3), "Jacques") { row =>
  new User(row.getInt("id"), row.getString("name"))
}
queryEvaluator.execute("INSERT INTO users VALUES (?, ?)", 1, "Jacques")

Note that sequences are handled automatically (i.e., you only need one question-mark (?)).

Run a query in a transaction for enhanced pleasure:

queryEvaluator.transaction { transaction =>
  transaction.select("SELECT ... FOR UPDATE", ...)
  transaction.execute("INSERT INTO users VALUES (?, ?)", 1, "Jacques")
  transaction.execute("INSERT INTO users VALUES (?, ?)", 2, "Luc")
}

The yielded transaction object implements the same interface as QueryEvaluator. Note that the transaction will be rolled back if you raise an exception.

Advanced Usage

For production-quality use of Querulous you'll want to set configuration options and layer-on more functionality. Here is the maximally configurable, if somewhat elaborate, way to instantiate a QueryEvaluator

import com.twitter.querulous.evaluator._
import com.twitter.querulous.query._
import com.twitter.querulous.database._

val queryFactory = new SqlQueryFactory
val apachePoolingDatabaseFactory = new apachePoolingDatabaseFactory(
  minOpenConnections:                 Int,      // minimum number of open/active connections at all times
  maxOpenConnections:                 Int,      // minimum number of open/active connections at all times
  checkConnectionHealthWhenIdleFor:   Duration, // asynchronously check the health of open connections every `checkConnectionHealthWhenIdleFor` amount of time
  maxWaitForConnectionReservation:    Duration, // maximum amount of time you're willing to wait to reserve a connection from the pool; throw an exception otherwise
  checkConnectionHealthOnReservation: Boolean,  // check connection health when reserving the connection from the pool
  evictConnectionIfIdleFor:           Duration  // destroy connections if they are idle for longer than `evictConnectionIfIdleFor` amount of time
)
val queryEvaluatorFactory = new StandardQueryEvaluatorFactory(apachePoolingDatabaseFactory, queryFactory)
val queryEvaluator = queryEvaluatorFactory(List("primaryhost", "fallbackhost1", "fallbackhost2", ...), "username", "password")

Now comes the fun part.

Query Decorators

Suppose you want timeouts around queries:

val queryFactory = new TimingOutQueryFactory(new SqlQueryFactory, 3.seconds)

Suppose you ALSO want to retry queries up to 5 times:

val queryFactory = new RetryingQueryFactory(new TimingOutQueryFactory(new SqlQueryFactory, 3000.millis), 5)

Suppose you have no idea what's going on and need some debug info:

val queryFactory = new DebuggingQueryFactory(new RetryingQueryFactory(new TimingOutQueryFactory(new SqlQueryFactory, 3.seconds), 5), println)

You'll notice, at this point, that all of these advanced features can be layered-on by composing QueryFactories. In what follows, I'll omit some layering to keep the examples terse.

Suppose you want to measure average and standard deviation of latency, and query counts:

val stats = new StatsCollector
val queryFactory = new StatsCollectingQueryFactory(new SqlQueryFactory, stats)

See the section [Statistics Collection] for more information.

Database Decorators

Suppose you want to measure latency around the reserve/release operations of the Database:

val stats = new StatsCollector
val databaseFactory = new StatsCollectingDatabase(new ApachePoolingDatabaseFactory(...), stats)

Suppose you are actually dynamically connecting to dozens of hosts (because of a sharding strategy or something similar) and you want to maintain proper connection limits. You can memoize your database connections like this:

val databaseFactory = new MemoizingDatabaseFactory(new ApachePoolingDatabaseFactory(...))

QueryEvaluator Decorators

Suppose you want to automatically disable all connections to a particular host after a certain number of SQL Exceptions (timeouts, etc.):

val queryEvaluatorFactory = new AutoDisablingQueryEvaluatorFactory(new StandardQueryEvaluatorFactory(databaseFactory, queryFactory))

Recommended Configuration Options

  • Set minActive equal to maxActive. This ensures that the system is fully utilizing the connection resource even when the system is idle. This is good because you will not be surprised by connection usage (and e.g., unexpectedly hit server-side connection limits) during peak load.
  • Set minActive equal to maxActive equal to the MySql connection limit divided by the number of instances of your client process
  • Set testIdle to 1.second or so. It should be substantially less than the server-side connection timeout.
  • Set maxWait to 10.millis--to start. In general, it should be set to the average experienced latency plus twice the standard deviation. Gather statistics!
  • Set minEvictableIdle to 5.minutes or more. It has no effect when minActive equals maxActive, but in case these differ you don't want excessive connection churning. It should certainly be less than or equal to the server-side connection timeout.

Statistics Collection

StatsCollector is actually just a trait that you'll need to implement using your favorite statistics collecting library. My favorite is Ostrich and you can write an adapter in a few lines of code. Here is one such adapter:

val stats = new StatsCollector {
  def incr(name: String, count: Int) = Stats.incr(name, count)
  def time[A](name: String)(f: => A): A = Stats.time(name)(f)
}
val databaseFactory = new StatsCollectingDatabaseFactory(new ApachePoolingDatabaseFactory(...), stats)

Installation

Maven

Add the following dependency and repository stanzas to your project's configuration

<dependency>
    <groupId>com.twitter</groupId>
    <artifactId>querulous</artifactId>
    <version>1.1.0</version>
</dependency>

<repository>
  <id>twitter.com</id>
  <url>http://www.lag.net/nest</url>
</repository>

Ivy

Add the following dependency to ivy.xml

<dependency org="com.twitter" name="querulous" rev="1.1.0"/>

and the following repository to ivysettings.xml

<ibiblio name="twitter.com" m2compatible="true" root="http://www.lag.net/nest/" />

Running Tests

Most of the tests are unit tests and are heavily mocked. However, some tests run database queries. You should change the username and password in config/test.conf to something that actually works for your system. Then, from the command line, simply run:

% ant test

Reporting problems

The Github issue tracker is {here}[http://github.com/nkallen/querulous/issues].

Contributors

  • Nick Kallen
  • Robey Pointer
  • Ed Ceaser
  • Utkarsh Srivastava

More Repositories

1

plasticity

TypeScript
2,909
star
2

cache-money

A Write-Through Cacheing Library for ActiveRecord
Ruby
996
star
3

arel

A Relational Algebra
Ruby
268
star
4

Rowz

A sample gizzard application
Scala
117
star
5

plasticity-blender-addon

Python
70
star
6

cachet

An HTTP Cache Proxy in Scala
67
star
7

effen

A jQuery plugin for Morphic programming
JavaScript
54
star
8

jquery-database

A relational database using <table> tags and jQuery
50
star
9

ss

A cross between Awk, a spreadsheet, and a relational database. A command line 'language' for statistical analysis.
40
star
10

gogaruco

An introduction to distributed computation with load balancers, proxies, and locality strategies.
Ruby
26
star
11

gitdb

gitdb
CoffeeScript
20
star
12

MetalSmith

Swift
12
star
13

LRJew

An LRU Simulator
Ruby
12
star
14

pseudw

Language learning software
CoffeeScript
10
star
15

TreePhysics

Generate 3d meshes of trees and simulate forces (like wind and gravity) on them
Swift
6
star
16

rkid---rcov-in-database

Rcov data stored in the database rather than html; includes a simple ActiveRecord model to access the data
Ruby
6
star
17

elephant

JavaScript
5
star
18

treebank-greek

Perseus Greek Treebank Data
CoffeeScript
5
star
19

perseus-nlp

CoffeeScript
4
star
20

Multiplayer

An implementation of multiplayer SceneKit/GameKit based on https://gafferongames.com/post/state_synchronization/
Swift
4
star
21

perseus-greco-roman

Perseus Greek & Roman texts
4
star
22

unp

Examples and Exercises from UNPv2
C
2
star
23

trigon

large left-handed Macropad with embedded 3d mouse and rotary encoder
HTML
2
star
24

houdini-prefs

Python
1
star
25

lexicon

1
star
26

treebank-latin

Perseus Latin Treebank Data
1
star
27

tagged

1
star
28

perseus-reader

CoffeeScript
1
star
29

putty

Playing with Netty
Scala
1
star
30

treebank

1
star