Permalink
Browse files

Idempotent document creation support, new HTTP api to generate UUIDs …

…and support in the couch.js library for using them. Creating uuids client side ensure that document creation happens only once, despite automatic network retries.

git-svn-id: https://svn.apache.org/repos/asf/incubator/couchdb/trunk@684092 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information...
1 parent a33cf09 commit f2275fd25463764ea059b32ffb689baeeebc1ca1 Damien F. Katz committed Aug 8, 2008
Showing with 89 additions and 10 deletions.
  1. +40 −10 share/www/script/couch.js
  2. +36 −0 share/www/script/couch_tests.js
  3. +13 −0 src/couchdb/couch_httpd.erl
View
50 share/www/script/couch.js
@@ -42,18 +42,14 @@ function CouchDB(name) {
this.save = function(doc, options) {
var req;
if (doc._id == undefined)
- req = request("POST", this.uri + encodeOptions(options), {
- body: JSON.stringify(doc)
- });
- else
- req = request("PUT", this.uri + encodeURIComponent(doc._id) + encodeOptions(options), {
- body: JSON.stringify(doc)
- });
+ doc._id = CouchDB.newUuids(1)[0];
+
+ req = request("PUT", this.uri + encodeURIComponent(doc._id) + encodeOptions(options), {
+ body: JSON.stringify(doc)
+ });
var result = JSON.parse(req.responseText);
if (req.status != 201)
throw result;
- // set the _id and _rev members on the input object, for caller convenience.
- doc._id = result.id;
doc._rev = result.rev;
return result;
}
@@ -91,14 +87,25 @@ function CouchDB(name) {
}
this.bulkSave = function(docs, options) {
+ // first prepoulate the UUIDs for new documents
+ var newCount = 0
+ for (var i=0; i<docs.length; i++) {
+ if (docs[i]._id == undefined)
+ newCount++;
+ }
+ var newUuids = CouchDB.newUuids(docs.length);
+ var newCount = 0
+ for (var i=0; i<docs.length; i++) {
+ if (docs[i]._id == undefined)
+ docs[i]._id = newUuids.pop();
+ }
var req = request("POST", this.uri + "_bulk_docs" + encodeOptions(options), {
body: JSON.stringify({"docs": docs})
});
var result = JSON.parse(req.responseText);
if (req.status != 201)
throw result;
for (var i = 0; i < docs.length; i++) {
- docs[i]._id = result.new_revs[i].id;
docs[i]._rev = result.new_revs[i].rev;
}
return result;
@@ -232,3 +239,26 @@ CouchDB.request = function(method, uri, options) {
req.send(options.body || "");
return req;
}
+
+CouchDB.uuids_cache = [];
+
+CouchDB.newUuids = function(n) {
+ if (CouchDB.uuids_cache.length >= n) {
+ var uuids = CouchDB.uuids_cache.slice(CouchDB.uuids_cache.length - n);
+ if(CouchDB.uuids_cache.length - n == 0) {
+ CouchDB.uuids_cache = [];
+ } else {
+ CouchDB.uuids_cache =
+ CouchDB.uuids_cache.slice(0, CouchDB.uuids_cache.length - n);
+ }
+ return uuids;
+ } else {
+ var req = CouchDB.request("POST", "/_uuids?count=" + (100 + n));
+ var result = JSON.parse(req.responseText);
+ if (req.status != 200)
+ throw result;
+ CouchDB.uuids_cache =
+ CouchDB.uuids_cache.concat(result.uuids.slice(0, 100));
+ return result.uuids.slice(100);
+ }
+ }
View
36 share/www/script/couch_tests.js
@@ -194,6 +194,42 @@ var tests = {
}
},
+ uuids: function(debug) {
+ var db = new CouchDB("test_suite_db");
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+ // a single UUID without an explicit count
+ var xhr = CouchDB.request("POST", "/_uuids");
+ T(xhr.status == 200);
+ var result = JSON.parse(xhr.responseText);
+ T(result.uuids.length == 1);
+ var first = result.uuids[0];
+
+ // a single UUID with an explicit count
+ xhr = CouchDB.request("POST", "/_uuids?count=1");
+ T(xhr.status == 200);
+ result = JSON.parse(xhr.responseText);
+ T(result.uuids.length == 1);
+ var second = result.uuids[0];
+ T(first != second);
+
+ // no collisions with 1,000 UUIDs
+ xhr = CouchDB.request("POST", "/_uuids?count=1000");
+ T(xhr.status == 200);
+ result = JSON.parse(xhr.responseText);
+ T( result.uuids.length == 1000 );
+ var seen = {};
+ for(var i in result.uuids) {
+ var id = result.uuids[i];
+ T(seen[id] === undefined);
+ seen[id] = 1;
+ }
+
+ // check our library
+ },
+
bulk_docs: function(debug) {
var db = new CouchDB("test_suite_db");
db.deleteDb();
View
13 src/couchdb/couch_httpd.erl
@@ -97,6 +97,8 @@ handle_request0(Req, DocumentRoot, Method, Path) ->
handle_replicate_request(Req, Method);
"/_restart" ->
handle_restart_request(Req, Method);
+ "/_uuids" ->
+ handle_uuids_request(Req, Method);
"/_utils" ->
{ok, Req:respond({301, [
{"Location", "/_utils/"}
@@ -148,6 +150,17 @@ handle_restart_request(Req, 'POST') ->
handle_restart_request(_Req, _Method) ->
throw({method_not_allowed, "POST"}).
+handle_uuids_request(Req, 'POST') ->
+ Count = list_to_integer(proplists:get_value("count", Req:parse_qs(), "1")),
+ % generate the uuids
+ UUIDs = [ couch_util:new_uuid() || _ <- lists:seq(1,Count)],
+ % send a JSON response
+ send_json(Req, {obj, [{"uuids", list_to_tuple(UUIDs)}]});
+
+handle_uuids_request(_Req, _Method) ->
+ throw({method_not_allowed, "POST"}).
+
+
% Database request handlers
handle_db_request(Req, Method, {Path}) ->

0 comments on commit f2275fd

Please sign in to comment.