When to Use Persevere: a Comparison with CouchDB and Others

By on November 18, 2008 10:24 am

Persevere and CouchDB are both new JSON-based storage systems, and have been receiving increasing focus. It may be informative to those interested in these new technologies to discuss the differences between these new database systems and what types of applications each is best suited for. While Persevere and CouchDB share an extremely similar JSON HTTP/REST interface and object structured data storage, there are important distinctions. Each of these different tools has a different target audience, and despite my obvious bias being the developer of Persevere, I hope to give at least some semi-objective differentiations between these tools.

Fundamentally, CouchDB is primarily focused on being a general purpose database that is an alternative to a relational database. Persevere encompasses data storage but focuses on Ajax applications, being a complete server-side data storage and application server for JavaScript-based web applications with tight integration with JavaScript. As such, Persevere might also be comparable to other JavaScript servers like Jaxer and Helma, and the distinction between these are worth noting as well. Here is a quick high-level comparison of the primary architectural focus of these different projects:

Project Description/Primary Use Case
CouchDB A JSON document based database for storing hierarchical object-style data structures using an HTTP interface with ACID lockless operation. This is primarily intended for being used by server-side technologies, and is not focused on direct browser access.
Persevere A JSON database integrated with a JavaScript environment for object persistence with HTTP interface for RESTful data interchange and RPC triggered code execution with Comet capabilities. Persevere is focused on browser-based client-UI-driven applications that utilize a web-based data and service oriented architecture between the browser and server. Persevere is built for data-centric Ajax/JavaScript-based applications with end-to-end JavaScript/JSON.
Helma Helma is a JavaScript server that also features a JavaScript environment with an object-oriented database for automatic data persistence. However, Helma differs from Persevere in that it is focused on a server-side MVC pattern and user interface generation instead of Persevere’s focus of enabling data access for rich browser-based clients.
Jaxer Jaxer is JavaScript server that is focused on recreating the browser’s API and environment (essentially running Firefox on the server) so that browser’s technologies can be utilized for server-side user interface generation. Since Jaxer allows server-side code to act like it’s in a browser, it can actually be used as a front-end for Persevere.

We will now take more in-depth look at the differences between Persevere and CouchDB (focusing on CouchDB due to it’s similarity to Persevere and its popularity).

Querying

An essential aspect of any database is its querying capabilities, and Persevere is differentiated from CouchDB by its ability to handle ad-hoc parameterized queries. Traditional relational databases utilize SQL for querying. SQL is inappropriate for a JSON-based database, the data structures are completely different, and for Ajax applications that are leveraging a HTTP interface directly from the browser, SQL is hopelessly insecure. CouchDB uses a view model which acts as an ongoing incremental map-reduce function, providing a constantly updated view of the database. From the HTTP interface different views can be accessed and data can be retrieved by key/index as well. The view model is well-suited for statically definable queries; job-style operations. On the other hand, Persevere uses JSONQuery, an extended version of JSONPath, as its query language which can be used to retrieve data from the database directly in the HTTP/REST interface. The difference here is that you can dynamically create a query with any arbitrarily complex expressions and any values. The workhorse of most applications is parametrized queries and these can easily be created with JSONQuery expressions and handled by Persevere.

Server-Side JavaScript Integration

Persevere is designed from the ground-up to have a consistent JavaScript to database integration. There is no need for object-relational mapping (ORM) layers, data is stored in the same structure that it is represented in JavaScript. The JavaScript environment has full orthogonal persistence capabilities. Data can updated by simply modifying properties with standard JavaScript syntax. Tables have corresponding JavaScript classes and every object in the table is treated as an instance of the class. Every class/table can have its own set of methods that are accessible as methods on the instance objects. For example, we can load an instance from a table in the JavaScript environment:

// create a new object in a table/class
var myObj = new MyClass();
// set a property, automatically persisted to the database
myObj.foo = "bar";
// this will return true
myObj instanceof MyClass 

Furthermore, you can also interact with these JavaScript methods through JSON-RPC calls. Thus client code can remotely execute server-side methods with direct integration with the data model. CouchDB does have a server-side JavaScript environment, but it is intended to be used to delegate map-reduce function processing, and is not available for server side RPCs, nor supports JavaScript class/table integration.

Security

Security and access control have been key features of Persevere from the start of the project; CouchDB also recently added access control. CouchDB provides two global access levels: Administrator and Reader. Persevere provides much finer-grained access control, it is possible to assign different access levels for different users to different tables, and even to different specific objects. Persevere’s security system is capability-based, and capabilities can be created that encompass a set of access levels to different objects or tables in the system. These capabilities can be individually granted to different users. CouchDB and Persevere both also support more programmatic control of security during object validation to handle more complex logic for access decisions.

Persevere provides an API for authentication from the web, and supports maintaining authentication with cookies (no manual manipulation of the requests is required).

Rich Data Structures

Both CouchDB and Persevere utilize JSON as the representation for data transfer and storage. However, Persevere also supports additional extensions of JSON for more complex data structures. Persevere utilizes JSON referencing to describe data structures with circular references, multiple references to single objects, cross-table references, and even remote URL references. Persevere additionally supports several JavaScript constructs that are not included in the JSON subset (JSON is a subset of JavaScript) including dates, functions, and NaN. Consequently, Persevere is designed to handle persisting virtually any data structure that can be created in a JavaScript environment.

Schema

CouchDB is explicitly schema-free, and no constraints are applied to documents. Persevere provides a flexible evolutionary approach to schemas, tables start schema-free, but schema constraints can be applied as desired to existing tables to create integrity guarantees. Partial constraints (to some properties) can be defined and modified at any point in time allowing any level of flexibility or integrity desired.

Scalability

Scalability and clustering has been one of the primary focuses of CouchDB: its name is an acronym for Cluster of Unreliable Commodity Hardware. CouchDB is certainly more mature in this category with its advanced clustering capabilities and heavy focus on extreme scalability. Clustering is certainly on the road map for Persevere. The REST architecture is designed for scaling across multiple machines and Persevere is well suited for adding this capability. In fact, with Persevere’s Comet notification system, clustering can and should be implemented almost entirely with its standard documented HTTP interface.

Comet Capabilities

With Persevere, it is possible to have a real-time view of data by subscribing to objects and receiving notification of changes. This capability is specifically designed for web applications. Notifications can be received from the browser using the REST Channels Comet interface. Persevere’s Comet capabilities are not limited to data changes, but any type of method call can serve as event for broadcast to clients. Persevere also supports Bayeux and its PubSub system. These Comet capabilities are exclusive to Persevere, and CouchDB has nothing that is similar.

Concurrency

Persevere and CouchDB both provide isolated transactions with lockless operation (objects are never locked). With CouchDB, each request corresponds to a single transaction, and by default Persevere behaves the same way. Persevere and CouchDB both support multi-operation transactions, and it is also possible to do multiple actions in one isolated transaction. For example, you could create a new object, update another object, and delete a third and then commit all three actions as a single transactional operation. CouchDB provides this capability through “bulk updates”. Persevere handles multiple action transactions by allowing multiple HTTP requests to be grouped together in a single transaction.

CouchDB provides the advantage of full ACID compliance. Persevere currently provides consistency, isolation, and durability, but is not truly atomic at this time. True atomicity is intended to be implemented in the future.

All object updates in CouchDB are preconditioned on providing the correct previous version number of the object that is being modified (optimistic concurrency). If the version number does not match, it is assumed to have been modified elsewhere and the operation will fail. Persevere provides a more flexible precondition system. Updates to objects can be made without preconditions to make it easier to get started. Preconditions can then be added, and any type of expression can be used as a precondition. In many situations predicating an update on a version number is a naive. Often an update is performed on the basis of various other, possibly more complex, object states. For example, if one is operating a translation service in order to translate text from English to Japanese, the application may wish to update the Japanese object on the precondition that the English version has not changed (rather than on the condition of the Japanese object not changing).

Cross-Domain Transports

From the browser, cross-domain requests require special techniques due to the same-origin policy that is applied to the XMLHttpRequest object. Persevere supports both JSONP (scripts with callbacks) and the window.name protocol for providing data for cross-domain web clients. CouchDB does not provide a facility for handling cross-domain requests.

Legacy Data Sources

Another unique capability of Persevere is that it will not only work with the default internal object database, but it can also be used to connect to existing SQL databases and even other remote RESTful HTTP servers as well. The configuration files are treated as data sources in the pluggable data source layer as well.

Binary/non-JSON Content Support

Both Persevere and CouchDB support the storage of non-JSON content in their databases. CouchDB treats non-JSON data as “attachments” within the JSON structure. On the other hand, Persevere provides a “File” table which holds the non-JSON entities, and with Persevere’s referencing capabilities, these entities can easily be included within any object in the system. Persevere builds on the REST architecture and allows these files to be used as an alternate representation of existing JSON objects. Content negotiation can then be used to retrieve these alternate format types for resources in Persevere. For example, one could define a JPEG image as an alternaten representation of a Person object in Persevere. To further support browser-based interactivity, Persevere supports the upload of files using standard browser forms. Files can be added and retrieved directly from browser.

Summary

Persevere and CouchDB’s JSON-based approach to database design is an exciting new avenue for data storage. CouchDB provides a compelling alternative to the relational database. Persevere’s leverages the JSON-based design to provide storage that is completely consistent with the object model used in the browser’s JavaScript environment, making it the perfect server for rich web applications based on a REST architecture, maintaining the REST principal of separating UI on the client-side from data storage on the server-side, and facilitating JavaScript based applications with uniform JavaScript/JSON data modeling from the client to the server.

Comments

  • Hey Kris,

    Just reading through your post and I wanted to point out that there’s a pretty big push to serve applications directly from CouchDB. The idea being that pure HTML+CSS+JS living in a CouchDB document makes distribution a simple matter of replicating a database to the client machine. Check out Chris Anderson’s blog at [1, 2] for a more thorough description.

    [1] http://jchris.mfdz.com/code/2008/11/my_couch_or_yours__shareable_ap
    [2] http://jchris.mfdz.com/code/2008/10/standalone_applications_with_co

  • @Paul: Yes, I would think CouchDB would want to do something like that, but of course this is an area where Persevere certainly outshines CouchDB, with it’s first class support for JavaScript functions (whereas CouchDB would need to treat JS as strings or attachments).

  • Just a quick correction on transactions in CouchDB. While it is true that transactions can never span across requests, it is possible to e.g. “create a new object, update another object, and delete a third and then commit all three actions as a single transactional operation” by packing those operations together in a “bulk update” request. Bulk updates can create, delete, or update any number of objects (request size/memory being the limit).

  • @Christopher: Thanks for the correction, I will update the page.

  • I think that I understand the significant differences between Persevere and CouchDB, however, I’m not sure I would agree that CouchDB should be viewed as simply an alternative to Relational databases. I think that CouchDB and Persevere both seem to be very different from relational databases. I think that it is important to highlight the differences – as in http://www.eflorenzano.com/blog/post/why-couchdb-sucks/

  • I have just read the article “Why CouchDB sucks” and here comes an excerpt which tackles a common use case in (social) web apps:

    “First, it doesn’t support transactions in the way that most people typically think about them. That means, enforcing uniqueness of one field across all documents is not safe. A classic example of this would be enforcing that a username is unique. You can check whether a username exists, and if not, create a new one. There is no guarantee, however, that between the time that your app has checked for its existence, and the time that you write the new user to the database, that some other instance of your app hasn’t beat you to that write.”

    How does Persevere behave with regard to this use case in May 2009 ? And with regard to the other perceived drawbacks of CouchDB described in the article ? I think in order to persuade potential Persevere users to commit themselves fully to its new, innovative, flexible and powerful approach, they should also know about such concrete drawbacks; which seem to be no esoteric use cases at all … And should be workaroundable, if needed …

  • @Kai: Yes, Persevere does support transactions, complete with the ability to define conditions that must be true in order for the transaction to commit (thus you can create a new user with the condition that no user with the given name exists).

  • Jack

    Hey want to know how do we do object to object relations?

  • @Jack: From the HTTP interface, you use JSON referencing to reference other objects, from the server side code you can assign an object as property of another object to make a reference.