4 ms·
What is the best approach to web app development in Python without a RDBMS?
Having read a couple threads [1] on dropping the RDBMS in favor of keeping data in RAM and logging to disk, I'm wondering what is a good setup for a typical Python web app.
There seem to be a number of options; memcached, BDB, ZopeDB, metakit, serialising into sqlite...
Any ideas?
1. [http://news.ycombinator.com/item?id=14605 http://news.ycombinator.com/item?id=14605], [http://news.ycombinator.com/item?id=16098 http://news.ycombinator.com/item?id=16098]
- mattculbreth 19y agoWhat kind of app are you building? I don't think you'll find a pre-packaged framework out there that works without a DB. I guess if I were doing this I'd use memcached and then I'd write to a shared file system somewhere for the writes. We use Pylons and SQLAlchemy/Elixir/PostgreSQL though and we're very pleased.
- jey 19y agoDepends, what are your requirements or what does your program do? RDBMSes are popular because they're easy and they perform decently for most answers to the previous question. And it may be that an RDBMS is the best answer for your application.
- dood 19y agoI should have been clearer in the question, I'm not looking for a solution to a specific problem, I'm looking for a general approach to building this kind of system, or an understanding of the benefits and trade-offs of different methods. I understand why RDBMS are normally used, and am comfortable with SQL, but I'm interested in the alternatives.
- jey 19y agoStart simple: store the data in-memory in whatever way makes sense, and just write a log file that contains pickled transactions that can be played back. Conceptual sketch of how I would do it follows. This isn't "the" way to do it, just "a" way to do it. # this is the utility method used by the rest of your code # to register a new user. maybe this is at module scope in # the User.py module def register_user(user): run_transaction(AddUserTransaction(user)) # this is the actual object that represents the # transaction class AddUserTransaction(Transaction): def __init__(self, user): Transaction.__init__(self) self.user = user # all Transaction objects have an apply() method def apply(self): Transaction.apply(self) # invoke base method # the following MyGlobalUserTable probably just # stores a couple dicts as indexes over the user info # e.g. it will have a dict by username, by user ID, etc MyGlobalUserTable.add_user(self.user) def run_transaction(t): # apply the transaction first, then write to the log. # since if it crashes while running the transaction, # you don't want to crash again when you play back the log. t.apply() transaction_log.append(t) Here transaction_log.append(t) is some method that will pickle the transaction and append it to some log file. You'll have multiple classes like AddUserTransaction, all derived from Transaction. When you crash and want to play back the transaction log, all you have to do is unpickle the Transaction-derived objects and call apply() on them in the same order. http://docs.python.org/lib/module-pickle.html http://docs.python.org/lib/module-pickle.html Caveats: -- This will eventually get to have too large a startup cost to play back all the transactions, when you have a huge number of transactions. You can fix it at that point, and you can just replay all the transactions to get the data imported into the new format. -- The above approach is horrid if you want to launch a new process for every single request. You'd have to replay the transaction log for each incoming request, you'd have a data coherency nightmare, etc. So if you want to use this approach, make sure you're using something that shares one process amongst all requests. You might also need to worry about whether your server is multithreaded and in that case deal with locking and other crap. I'd suggest going simple with something like Twisted's single-request-at-a-time approach. This isn't as bad as it sounds at first; you just write everything to finish ASAP, and if there's something you need to come back to, you just request the Twisted Reactor to schedule an event. If you have some big blocking thing to do, do it in another thread then notify the Reactor when it's complete (and have the Reactor schedule an event for your code to be notified that the big blocking thing finished). As you can see, it's not totally trivial and general purpose like the RDBMS + ThingThatGeneratesNastySQLFromObjects approach (aka ORM). But you get more flexibility in your interfaces, and lower overhead. While it can be rewarding and simplifying to work in this way, it could also can lead to over-engineering and/or lost time and effort if you aren't already intimately familiar with the steps in this approach.
- deleted 19y ago[deleted]
- mxh 19y agoTwisted is an interesting framework, though a little lower-level than others. I think you could build a self-contained, in-memory solution around it, as DBs aren't particularly central to its operation.
- codeslinger 19y agoIt sounds to me like the closest thing to what you want is a library for Object Prevalence <http://en.wikipedia.org/wiki/Object_prevalence> http://en.wikipedia.org/wiki/Object_prevalence>. There is one that I know of for Python called Pypersyst <http://pypersyst.org/> http://pypersyst.org/>. There is also an IBM DeveloperWorks article about this library by one of the authors, as well <http://www.ibm.com/developerworks/library/l-pypers.html> http://www.ibm.com/developerworks/library/l-pypers.html>. Hope that helps :)
- Hexayurt 19y agohttp://itamarst.org/software/cog/ http://itamarst.org/software/cog/ Cog is the Checkpointed Object Graph object database, providing semi-transparent persistence for large sets of interrelated Python objects. It handles automatic loading of objects on reference, and saving of modified objects back to disk. Reference counting is used to automatically remove no longer referenced objects from storage, and objects will be automatically be attached to the database if a persistent object references them. ====== I looked this package over a few years ago, and I think it got an awful lot of things right... but not enough. It's worth examining the design if you want to understand the intricacies of non-RDBMS approaches. A lot of thought went into it.
- palish 19y agoYou might want to ask yourself, "Why am I choosing to not use an RDBMS?" If the answer is "because I know it will save time" or "because I'd like to explore the possibility", more power to you. I wholeheartedly encourage it. If the answer is anything else then you should just use one, because that path will save you time (possibly a lot of time). Check out Rails. The ORM is so well done that it feels like you aren't using a database at all.
- dood 19y agoYes, I'm pretty much checking out the solution-space as a whole. I'll probably end up slinking back to SQL, but at least I'll know why.
- kashif 19y agoschevo.org integrates well with pylons
- dood 19y agoschevo does look interesting, I'll have to give it a closer look. Though as far as I can tell it isn't widely used... how is your experience of it?
- kashif 19y agoThe documentation is limited. But it is very powerful and easy to use. The support on the IRC is excellent although the response time is around a day.