It's a form of the Command design pattern and has some nice properties. You can get transparent thread synchronization by executing queries in parallel but serializing commands that modify state. For web apps, the HTTP request offers a natural representation for commands (but of course you'd want to strip them down to their essence). You can get fault-tolerance and scalability by feeding the command stream to replica servers. (State-changing commands must be executed by the master server but queries can be load-balanced across the replicas.) And if you keep the journals around, you have a complete history of the application's state.
EDIT: See also http://www.advogato.org/article/398.html
And appending data to a log is essentially an ideal disk access pattern.
Of course this can't be said for the snapshots, but you can offload that task to a replica server.
it's often also used as an LRU cache on top of a normal database so that you can still get the benefits of persistence and rich queries but use memcached to lower the number of hits to the disk/DB.
Also when you use memchached as a db cache, be preprared to lose some transactional properies of the database storage.
"When you have the users on your server, you don't have to rely on benchmarks, for example. Benchmarks are simulated users. With server-based software, you can watch actual users. To decide what to optimize, just log into a server and see what's consuming all the CPU. And you know when to stop optimizing too: we eventually got the Viaweb editor to the point where it was memory-bound rather than CPU-bound, and since there was nothing we could do to decrease the size of users' data (well, nothing easy), we knew we might as well stop there."
Couldn't you have done better if, instead of keeping all hash entries in memory, you kept them on disk and used memory as an LRU buffer? It seems like you could have fit more users on a server that way.
It's snappy and with very little overhead.
LOL keep downmodding me. I'd love to see the hashmap-based transaction handling. Was it as good as this:
No one says that if you need a relational database you should reimplement it yourself - just that the DB is not the only solution for all web back ends.
Most problems I've worked on had to take scaling, concurrency, searching/lookup, report generation (joining), etc, into account...all of which a dbms gives you out of the box. Maybe we're dealing with _very_ different kinds of problems.
The problem was completely parallelizable; stores didn't interact.
There are all sorts of apps that use mem/fs for storage. Like mail servers, news servers, squid servers, static http servers, log files, and sql servers (discounting direct io)
rdbms are (usually) just another layer ontop of fs. A very usefull layer if you need what they offer. But if not just a layer of complexity and wasted mem/cpu.
Modern fs have journaling and smart buffering/caching. They kind of rock and discounting them out of hand is a mistake.
Notes
1. I'd suggest BDB, but it seems to suck. YMMV, always benchmark it yourself.
The other advantage that using memory/fs has over a RDBMS for most simple applications is that unless you are doing complicated joins you will keep things simple and avoid introducing an additional layer of complexity and point of failure into your app.
BDB seems pretty flexible in policy decisions, e.g. the master can say the commit is complete when it hears back from N replicas depite not having written to disc itself yet.
DBMS are optimized for the fastest possible disk I/O. At their very nature they are also designed with data integrity in mind. It would be a joke not to use one if you are handling important data where persistence is even remotely a factor.
As you grow you'll be glad you implemented the DB code as it will scale much better. You'll also be able to do Analysis easier which can be very important in learning about usage of your product.
But I'm definitely a fan of utilizing ram as much as is allowed in the interest of speed. Try to strike a good balance between data integrity and performance...
I think that's disputable. DBMSes are designed for a number of considerations and it's not hard to demonstrate how alternatives can outperform a DBMS in terms of disk I/O and general performance.
Consider an HTTP request that modifies records. A typical DBMS-backed app will write all the changes to disk whereas a prevalent system (AKA object prevalence) need only write "POST /someurl arg1=value1:arg2=value2:..." or some equivalent. The data is updated in RAM with a write to only one or two disk sectors in the majority of cases, no communication with a DBMS, no construction and parsing of SQL, and none of the other overhead. A typical prevalent system will be orders of magnitude faster than the DBMS-backed equivalent, and simpler to boot.
Object prevalence doesn't offer a query language and has different scalability considerations, but it would take an absurdly broken design for a DBMS to outperform it.
I don't see how that could take more time than doing both the normal program data structures and the DB.
The tricky part would be the synchronization and transactional aspects, and if the system became sufficiently distributed that part could get fairly nasty.
The entirety of this thread is questioning the pervasive use of databases in web applications. There are some where it makes excellent sense (accounting, CRM, ERP, the stuff that's been the strong-hold of Oracle and SAS for years). And others (wikis, blogs, forums, photo galleries, etc.) where it may not make good sense, because you introduce hundreds of unnecessary operations and significant additional database support code. That's not to say a database is never the right solution for these problems (when your problem starts looking relational, you should start looking at relational databases, because your Ruby or Python or Lisp implementation is going to be worse than what PostgreSQL or MySQL have). And, as someone else mentioned (but got modded down)...when you start building your own flaky transactional layer, then it may be time to consider a database that has good transactions support.
I had to write an external "chat history" module (i.e., always send the last N lines of a conversation to a client as soon as client joins) for an IRC server, in python. (it's a long story)
Your standard RDBMS solution would probably involve the ircd logging directly or indirectly to a database, with a python script handling requests and doing selects out of that db. On a network with thousands of users, this will load a db machine down with IO operations. This also has to happen quickly, so that new text is added to the history log in near real-time. Batching is probably out of the question.
My solution was to have the ircd pass off its strings to another program (in this case, I used python) via a named pipe (this could also have been done via a socket, to aggregate multiple servers or do other neat tricks), and then have the logger app load these into memory - in this case, a giant python dictionary keyed on channel name which pointed to a constantly-updated ring buffer class for each channel. This ring buffer held the last N lines of conversation.
Writes were simple - h[c].write('text')
Lookups were simple - h[c].get()
Fast writes, fast reads, and disk i/o was batched for efficient logging to disk, at which point you deal with the data in batches and maybe store it into a db if you want to do something more advanced like with SQL.
Of course this could all be done with C within the ircd itself as well, using a hash table and ring buffers as well.
It was actually much faster (and logically simpler) than writing a DB solution, and I'm certainly no master hacker.
One of my spare-time projects, lispdoc.com, uses an in-memory approach (although to be fair it is only indexing a few hundred MB of static data, so it's no effort to keep it all in memory on a single box).
http://radar.oreilly.com/archives/2006/04/database_war_stories_2_bloglin.html
A few months ago, I created a silly little sudoku site, then blogged about how I did it with no database backend (man, I got flamed). Truth is, I really didn't need one but lots of people couldn't see past the fact that I wasn't using the conventional database approach. In their minds, there was simply no reason why I should have used a few flat files.
The whole idea of databases and flat files is such a polarizing topic. It's terribly interesting.
What DB does news.YC use?
Some considerations that come to mind: 1) Relational databases bring a form of 'automatic' documentation to a project in that somebody who hasn't touched it before can expect to make a reasonable start on understanding it by using known tools to look at the schema. 2) You get powerful hot-patching tools with a relational database (sqlplus, psql, or similar) that have the safety of things like foreign key constraints. 3) Major version upgrades. As you're developing you can track db changes by writing change scripts. Then when you do your upgrade you can 'pull the lever'. There's nothing stopping you from doing this with any other structure, it's just something to think about.
- In AllegroCache/AllegroStore, the class definition is the schema definition; so if the newcomer understands class structures he understands the schema. - Hot-patching can be done through plain-old REPL. At least AllegroStore the system took care of key consistency (if it is explicit to the system). - In AllegroStore schema change is handled as class change (of persistent instances). So you can use usual MOP to write update function, which corresponds to the change scripts.
The main difficulty, compared to RDBMS, seemed to come from the fact that the stored objects directly formed a graph, not a table. Some people seemed to have a hard time to "think" directly in graphs, and preferred table analogy.
AllegroGraph tutorial: http://www.franz.com/products/allegrograph/doc/lisp/agraph-tutorial.html
my little lispy wrapper to SPARQL: http://abcl-web.sourceforge.net/rdf.html (i like it somewhat more that Prolog used with AllegroGraph :)
From: http://paulgraham.com/vwfaq.html
"What database did you use?
We didn't use one. We just stored everything in files. The Unix file system is pretty good at not losing your data, especially if you put the files on a Netapp.
It is a common mistake to think of Web-based apps as interfaces to databases. Desktop apps aren't just interfaces to databases; why should Web-based apps be any different? The hard part is not where you store the data, but what the software does.
While we were doing Viaweb, we took a good deal of heat from pseudo-technical people like VCs and industry analysts for not using a database-- and for using cheap Intel boxes running FreeBSD as servers. But when we were getting bought by Yahoo, we found that they also just stored everything in files-- and all their servers were also cheap Intel boxes running FreeBSD.
(During the Bubble, Oracle used to run ads saying that Yahoo ran on Oracle software. I found this hard to believe, so I asked around. It turned out the Yahoo accounting department used Oracle.)"
The database landscape was different in 1995. There were no good free relational databases, and no cheap Oracle licenses.
Lisp makes it very easy to write out any data structure. (Most Scheme implementations can only write non-circular structures easily, but Viaweb used CL). So if your app is amenable to using flat files, coding storage/retrieval is trivial.
So basically, if you're writing an app with lots of separate datasets, flat files are a viable option. If you're using Lisp, they're an easy option. If it's 1995, they're a cheap option.
Ideally you want to code the person accessors so that they still specific externally
person-name person-age
the rest of the program uses these so it becomes easy to move person to a different structure if that's what you want .
You want to write some function to read a hash-table and write on to disc. In lisp this is trivial, but most languages have some sort of serializer. Then a function to load all the hash tables up when you start up.
If you're using s-exprs make your structure a list where the first element is a version identifier. If you aren't using s-exprs then use some other extensible format like XML.
How do you file-advocates solve that problem?
What would you use for firefox extensions? I suppose I can't connect to a database with Javascript.
If the file is small, I write to a tmp file then rename.
If multiple files are involved, I write new data to fresh files, then update the "master" file atomically that has pointer to the data files.
If lots of data is constantly added, I just keep appending to a file, but design the format so that I can always find the record boundary even if the last append was chopped by server crash.
However, memory is so cheap now that you could probably get away with keeping all of your business objects in memory, having them write-through to disk for persistence. I imagine that you would have to move business objects around the servers to decrease messaging latency as the connectivity between objects changes.
I don't know of any frameworks that use this approach.
Where you will find a benefit to using a RDBMS is in heavy correlation, multilevel sorting especially when you're talking about very large data sets. This is one of the key problem that RDBMS address. If your requirements also say anything about atomicity and integrity of the data it may be a better investment in the long run to go with an existing database solution that had a couple decades to work out those details for you.
I don't mean this to argue that a db is always good, but I'd guess one of the reasons that people use a database even when it's inappropriate is precisely because it seems less complex.
Of course, if you're using a bunch of elaborate ORM stuff, I suppose it might build your data structures without you ever really seeing them. Is that what you meant?
HDF5
Mnesia
Kx