more README fixups

This commit is contained in:
andrew fabbro 2016-09-01 09:55:40 -07:00
parent 9417980e33
commit 9d5d31459c
1 changed files with 18 additions and 18 deletions

View File

@ -2,7 +2,7 @@
gorqlite is a golang client for rqlite that abstracts API interactions into a stateful connection.
It is not a database/sql driver (read below for why this is impossible) but instead provides similar abstractions, such as Next()/Scan(), Open()/Exec()/Close(), etc. Additionally, many rqlite-specific features are implemented.
It is not a database/sql driver (read below for why this is impossible) but instead provides similar abstractions, such as `Next()`/`Scan()`, `Open()`/`Exec()`/`Close()`, etc. Additionally, many rqlite-specific features are implemented.
## Status
@ -10,25 +10,25 @@ gorqlite should be considered alpha until more testers share their experiences.
## Features
* Abstracts the rqlite http API interaction - the POSTs, JSON handling, etc. You submit your SQL and get back an interator with familiar database/sql semantics (Next(), Scan(), etc.) or a map[column name as string]interface{}.
* Abstracts the rqlite http API interaction - the POSTs, JSON handling, etc. You submit your SQL and get back an interator with familiar database/sql semantics (`Next()`, `Scan()`, etc.) or a `map[column name as string]interface{}.
* Timings and other metadata (e.g., num rows affected, last insert ID, etc.) is conveniently available and parsed into appropriate types.
* A connection abstraction allows gorqlite to discover and remember the rqlite leader. gorqlite will automatically try other peers if the leader is lost, enabling fault-tolerant API operations.
* Timeout can be set on a per-Connection basis to accomodate those with far-flung empires.
* Use familiar database URL connection strings to connection, optionally including rqlite authentication and/or specific rqlite consistency levels.
* Only a single node needs to be specified in the connection. gorqlite will talk to it and figure out the rest of the cluster from its redirects and status API.
* Support for several rqlite-specific operations:
** Leader() and Peers() to examine the cluster.
** SetConsistencyLevel() can be called at any time on a connection to change the consistency level for future operations.
** Timing() can be called on a per-result basis to retrieve the timings information for executed operations as float64, per the rqlite API.
* Trace(io.Writer)/Trace(nil) can be used to turn on/off debugging information on everything gorqlite does to a io.Writer of your choice.
** `Leader()` and `Peers() to examine the cluster.
** `SetConsistencyLevel() can be called at any time on a connection to change the consistency level for future operations.
** `Timing()` can be called on a per-result basis to retrieve the timings information for executed operations as float64, per the rqlite API.
* `Trace(io.Writer)`/`Trace(nil)` can be used to turn on/off debugging information on everything gorqlite does to a io.Writer of your choice.
* No external dependencies. Uses only standard library functions.
## Install
go get github.com/raindog308/gorqlite
`go get github.com/raindog308/gorqlite`
## Examples
`
```
// these URLs are just generic database URLs, not rqlite API URLs,
// so you don't need to worry about the various rqlite paths ("/db/query"), etc.
// just supply the base url and not "db" or anything after it.
@ -111,7 +111,7 @@ gorqlite.TraceOn(os.Stderr)
// turn off
gorqlite.TraceOff()
`
```
## Important Notes
If you use access control, any user connecting will need the "status" permission in addition to any other needed permission. This is so gorqlite can query the cluster and try other peers if the master is lost.
@ -144,23 +144,23 @@ The chief reasons a proper database/sql driver is not possible are:
* As a consequence, there is no rollback.
* The statement parsing/preparation API is not exposed at the SQL layer by sqlite, and hence it's not exposed by rqlite. What this means is that there's no way to prepare a statement ("INSERT INTO superheroes (?,?)") and then later bind executions to it. (In case you're wondering, yes, it would be possible for gorqlite to include a copy of sqlite3 and use its engine, but the sqlite C call to sqlite3_prepare_v2() will fail because a local sqlite3 won't know your DB's schemas and the sqlite3_prepare_v2() call validates the statement against the schema. We could open the local sqlite .db file maintained by rqlite and validate against that, but there is no way to make a consistency guarantee between time of preparation and execution, especially since the user can mix DDL and DML in a single transaction).
* The statement parsing/preparation API is not exposed at the SQL layer by sqlite, and hence it's not exposed by rqlite. What this means is that there's no way to prepare a statement (`"INSERT INTO superheroes (?,?)"`) and then later bind executions to it. (In case you're wondering, yes, it would be possible for gorqlite to include a copy of sqlite3 and use its engine, but the sqlite C call to `sqlite3_prepare_v2()` will fail because a local sqlite3 won't know your DB's schemas and the `sqlite3_prepare_v2()` call validates the statement against the schema. We could open the local sqlite .db file maintained by rqlite and validate against that, but there is no way to make a consistency guarantee between time of preparation and execution, especially since the user can mix DDL and DML in a single transaction).
* So we've turned off Begin(), Rollback, and Commit(), and now we need to turn off Prepare().
* So we've turned off `Begin()`, `Rollback()`, and `Commit(), and now we need to turn off `Prepare()`.
* As a consequence, there is no point in having statements, so they are unsupported. At this point, so much of the database/sql API is returning errors.New("NOT IMPLEMENTED") that we might as well write an rqlite-specific library.
* As a consequence, there is no point in having statements, so they are unsupported. At this point, so much of the `database/sql` API is returning `errors.New("NOT IMPLEMENTED")` that we might as well use an rqlite-specific library.
## Other Design Notes
In database/sql, Open() doesn't actually do anything. You get a "connection" that doesn't connect until you Ping() or send actual work. In gorqlite's case, it needs to connect to get cluster information, so this is done immediately and automatically open calling Open(). By the time Open() is returned, gorqlite has full cluster info.
In `database/sql`, `Open() doesn't actually do anything. You get a "connection" that doesn't connect until you `Ping()` or send actual work. In gorqlite's case, it needs to connect to get cluster information, so this is done immediately and automatically open calling `Open()`. By the time `Open()` is returned, gorqlite has full cluster info.
Just like database/sql connections, a gorqlite connection is not threadsafe. Don't treat them like expensive resources or build pools or things like that. Create gorqlite connections with wild abandon because they're simply a small struct of config information with a bunch of methods. No actual work or network discovery is even done until you use one, and Clone() allows you to avoid discovery altogether. Go make as many gorqlite connections as you wish, but do not share them amongst your goroutines.
Just like `database/sql` connections, a gorqlite connection is not threadsafe.
Close() will set a flag so if you try to use the connection afterwards, it will fail. But otherwise, you can merrily let your connections be garbage-collected with no harm, because they're just configuration tracking bundles and everything to the rqlite cluster is stateless. Indeed, the true reason that Close() exists is the author's feeling that if you open something, you should be able to close it. So why not GetConnection() then instead of Open()? Or GetClusterConfigurationTrackingObject()? I don't know. Fork me.
`Close()` will set a flag so if you try to use the connection afterwards, it will fail. But otherwise, you can merrily let your connections be garbage-collected with no harm, because they're just configuration tracking bundles and everything to the rqlite cluster is stateless. Indeed, the true reason that `Close() exists is the author's feeling that if you open something, you should be able to close it. So why not `GetConnection()` then instead of `Open()`? Or `GetClusterConfigurationTrackingObject()`? I don't know. Fork me.
Leader() and Peers() will both cause gorqlite to reverify its cluster information before return. Note that if you call Leader() and then Peers() and something changes in between, it's possible to get inconsistent answers.
`Leader()` and `Peers()` will both cause gorqlite to reverify its cluster information before return. Note that if you call `Leader()` and then `Peers()` and something changes in between, it's possible to get inconsistent answers.
Since "weak" consistency is the default rqlite level, it is the default level for the client as well. The user can change this at will (either in the connection string or via SetConsistencyLevel(), and then the new level will apply to all future calls).
Since "weak" consistency is the default rqlite level, it is the default level for the client as well. The user can change this at will (either in the connection string or via `SetConsistencyLevel()`, and then the new level will apply to all future calls).
## Tests
@ -180,5 +180,5 @@ These can overridden using the environment variables:
GORQLITE_TEST_TABLE=some_other_table
## Pronunciation
rqlite is supposed to be pronounced "ree qwell lite". So you could pronounce gorqlite as either "go ree kwell lite" or "gork lite". The Klingon in me prefers the latter. Really, isn't rqlite just the kind of battle-hardened, lean and mean system Klingons would use? Qapla'!
rqlite is supposed to be pronounced "ree qwell lite". So you could pronounce gorqlite as either "go ree kwell lite" or "gork lite". The Klingon in me prefers the latter. Really, isn't rqlite just the kind of battle-hardened, lean and mean system Klingons would use? **Qapla'!**