On Wed, Jun 4, 2014 at 10:22 AM, Howard Chu hyc@symas.com wrote:
Brian G. Merrell wrote:
Hi all,
First, I'm having trouble finding resources to answer a question like this myself, so please forgive me if I've missed something.
Thanks. I did see and skim the API portion of the docs before asking, but I was just having trouble knowing how the pieces fit together to solve a problem.
I'm considering using LMDB (versus LevelDB) for a project I'm working on
where I'll be receiving a high volume (hundreds per second) of high priority requests (over HTTP) and issuing multiple (<10) database queries per request.
I'll also have a separate process receiving updates for the data and writing to the database. This will happen often (several times a minute, perhaps), but the priority is much lower than the read requests.
LMDB appealed to me because of the read performance and that I could have one processing reading data from LMDB and another process writing data updates to LMDB.
For proof of concept, I hacked up the following (I'll use pseudocode since I used the Go bindings for my actual programs, and hopefully my question is sufficiently abstract not to matter):
Process 1, the writer, simply writes a random integer (from 0 to 1000) to
a defined set of keys:
env = NewEnv() env.Open("/tmp/foo", 0, 0664) txn = env.BeginTxn(nil, 0) dbi = txn.DBIOpen(nil, 0) txn.Commit()
txn = env.BeginTxn(nil, 0) n_entries = 5 for i = 0; i < n_entries; i++ { key = sprintf("Key-%d", i) val = sprintf("Val-%d", rand.Int(1000)) txn.Put(dbi, key, val, 0) } txn.Commit() env.DBIClose(dbi) env.Close()
Process 2, the reader, simply loops forever and does random access reads on the data from process 1 (I won't benefit from a cursor for my actual problem), and prints out that data occasionally:
env = NewEnv() env.Open("/tmp/foo", 0, 0664) while { txn = BeginTxn(nil, 0) dbi = txn.DBIOpen(nil, 0) txn.Commit() for i = 0; i < n_entries; i++ { key = sprintf("Key-%d", i) val = txn.Get(dbi, key) print("%s: %s", key, value) } env.DBIClose(dbi) sleep(5) }
So my high level question is: What am I doing wrong? This seems to work OK, but a lot of it was guesswork, so I'm sure I'm doing some silly things.
Your reader process should be using read transactions.
OK, I interpret this as meaning that I need to pass the MDB_RDONLY flag to mdb_txn_begin. Is that correct?
For example, first I put the BeginTxn() and DBIOpen() calls in process 2
outside of the while loop, but when I did that, I never saw the updates values upon running process 1 simultaneously. In my real-world application, it seems like adding these calls to every request (to be sure the data being read is up-to-date) could be an unnecessary performance penalty.
In the actual LMDB API read transactions can be reused by their creating thread, so they are zero-cost after the first time. I don't know if any of the other language wrappers leverage this fact.
This helps a lot. I will investigate what the case is with gomdb.
Opening a DBI only needs to be done once per process. Opening per transaction would be stupid, like reopening a file handle on every request.
I suspected so. The fact that mdb_dbi_open takes a transaction had me confused a bit, because I thought I would need to pass in the new transaction every time I got a transaction from mdb_txn_begin.
I've refactored the reader to look like this:
env = NewEnv() env.Open("/tmp/foo", 0, 0664) txn = BeginTxn(nil, mdb.RDONLY) // parent txn is the nil arg dbi = txn.DBIOpen(nil, 0) txn.Abort()
while { txn = BeginTxn(nil, mdb.RDONLY) // parent txn is the nil arg for i = 0; i < n_entries; i++ { key = sprintf("Key-%d", i) val = txn.Get(dbi, key) print("%s: %s", key, value) } txn.Commit() sleep(5) } env.DBIClose(dbi)
Now, I guess the big question that BeginTxn inside the loop is zero-cost.
Thanks for the tips so far Howard; it has been very helpful.
I was suspect there are flags that I can/should be using, but I'm not
sure.
Thanks for any input.
Brian
-- -- Howard Chu CTO, Symas Corp. http://www.symas.com Director, Highland Sun http://highlandsun.com/hyc/ Chief Architect, OpenLDAP http://www.openldap.org/project/
Brian