Skip to content

Commit

Permalink
Add utc_id_suffix UUID algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
NorthNick authored and rnewson committed Aug 6, 2012
1 parent ce7204b commit 5ab712a
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 4 deletions.
4 changes: 4 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ Test Suite:
* Improved tracebacks printed by the JS CLI tests
* Improved the reliability of a number of tests

UUID Algorithms:

* Added the utc_id algorithm.

Version 1.2.1
-------------

Expand Down
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This version has not been released yet.
* Added Server-Sent Events protocol to db changes API.
* Moved the JS test suite to the CLI
* Make password hashing synchronous when using the /_config/admins API.
* Added utc_id UUID algorithm.

Version 1.2.1
-------------
Expand Down
5 changes: 5 additions & 0 deletions etc/couchdb/default.ini.tpl.in
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,12 @@ _view = {couch_mrview_http, handle_view_req}
; random prefix is regenerated and the process starts over.
; utc_random - Time since Jan 1, 1970 UTC with microseconds
; First 14 characters are the time in hex. Last 18 are random.
; utc_id - Time since Jan 1, 1970 UTC with microseconds, plus utc_id_suffix string
; First 14 characters are the time in hex. uuids/utc_id_suffix string value is appended to these.
algorithm = sequential
; The utc_id_suffix value will be appended to uuids generated by the utc_id algorithm.
; Replicating instances should have unique utc_id_suffix values to ensure uniqueness of utc_id ids.
utc_id_suffix =

[stats]
; rate is in milliseconds
Expand Down
27 changes: 26 additions & 1 deletion share/www/script/test/uuids.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,29 @@ couchTests.uuids = function(debug) {
utc_testfun
);

};
// Test utc_id uuids
var utc_id_suffix = "frog";
var suffix_testfun = function() {
xhr = CouchDB.request("GET", "/_uuids?count=10");
T(xhr.status == 200);
result = JSON.parse(xhr.responseText);
for(var i = 1; i < result.uuids.length; i++) {
T(result.uuids[i].length == 14 + utc_id_suffix.length);
T(result.uuids[i].substring(14) == utc_id_suffix);
T(result.uuids[i-1] < result.uuids[i], "utc_id_suffix uuids are ordered.");
}
};

run_on_modified_server([{
"section": "uuids",
"key": "algorithm",
"value": "utc_id"
}, {
"section": "uuids",
"key": "utc_id_suffix",
"value": utc_id_suffix
}],
suffix_testfun
);

};
10 changes: 9 additions & 1 deletion src/couchdb/couch_uuids.erl
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@ random() ->
list_to_binary(couch_util:to_hex(crypto:rand_bytes(16))).

utc_random() ->
utc_suffix(couch_util:to_hex(crypto:rand_bytes(9))).

utc_suffix(Suffix) ->
Now = {_, _, Micro} = now(),
Nowish = calendar:now_to_universal_time(Now),
Nowsecs = calendar:datetime_to_gregorian_seconds(Nowish),
Then = calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}),
Prefix = io_lib:format("~14.16.0b", [(Nowsecs - Then) * 1000000 + Micro]),
list_to_binary(Prefix ++ couch_util:to_hex(crypto:rand_bytes(9))).
list_to_binary(Prefix ++ Suffix).

init([]) ->
ok = couch_config:register(
Expand All @@ -53,6 +56,8 @@ handle_call(create, _From, random) ->
{reply, random(), random};
handle_call(create, _From, utc_random) ->
{reply, utc_random(), utc_random};
handle_call(create, _From, {utc_id, UtcIdSuffix}) ->
{reply, utc_suffix(UtcIdSuffix), {utc_id, UtcIdSuffix}};
handle_call(create, _From, {sequential, Pref, Seq}) ->
Result = ?l2b(Pref ++ io_lib:format("~6.16.0b", [Seq])),
case Seq >= 16#fff000 of
Expand Down Expand Up @@ -88,6 +93,9 @@ state() ->
random;
utc_random ->
utc_random;
utc_id ->
UtcIdSuffix = couch_config:get("uuids", "utc_id_suffix", ""),
{utc_id, UtcIdSuffix};
sequential ->
{sequential, new_prefix(), inc()};
Unknown ->
Expand Down
20 changes: 20 additions & 0 deletions test/etap/041-uuid-gen-id.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
; Licensed to the Apache Software Foundation (ASF) under one
; or more contributor license agreements. See the NOTICE file
; distributed with this work for additional information
; regarding copyright ownership. The ASF licenses this file
; to you under the Apache License, Version 2.0 (the
; "License"); you may not use this file except in compliance
; with the License. You may obtain a copy of the License at
;
; http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing,
; software distributed under the License is distributed on an
; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
; KIND, either express or implied. See the License for the
; specific language governing permissions and limitations
; under the License.

[uuids]
algorithm = utc_id
utc_id_suffix = bozo
33 changes: 31 additions & 2 deletions test/etap/041-uuid-gen.t
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ seq_alg_config() ->
utc_alg_config() ->
test_util:source_file("test/etap/041-uuid-gen-utc.ini").

utc_id_alg_config() ->
test_util:source_file("test/etap/041-uuid-gen-id.ini").

% Run tests and wait for the gen_servers to shutdown
run_test(IniFiles, Test) ->
{ok, Pid} = couch_config:start_link(IniFiles),
Expand All @@ -40,7 +43,7 @@ run_test(IniFiles, Test) ->
main(_) ->
test_util:init_code_path(),
application:start(crypto),
etap:plan(6),
etap:plan(9),

case (catch test()) of
ok ->
Expand All @@ -63,6 +66,7 @@ test() ->
run_test([default_config()], TestUnique),
run_test([default_config(), seq_alg_config()], TestUnique),
run_test([default_config(), utc_alg_config()], TestUnique),
run_test([default_config(), utc_id_alg_config()], TestUnique),

TestMonotonic = fun () ->
etap:is(
Expand All @@ -73,6 +77,7 @@ test() ->
end,
run_test([default_config(), seq_alg_config()], TestMonotonic),
run_test([default_config(), utc_alg_config()], TestMonotonic),
run_test([default_config(), utc_id_alg_config()], TestMonotonic),

% Pretty sure that the average of a uniform distribution is the
% midpoint of the range. Thus, to exceed a threshold, we need
Expand All @@ -94,7 +99,18 @@ test() ->
"should roll over every so often."
)
end,
run_test([default_config(), seq_alg_config()], TestRollOver).
run_test([default_config(), seq_alg_config()], TestRollOver),

TestSuffix = fun() ->
UUID = binary_to_list(couch_uuids:new()),
Suffix = get_suffix(UUID),
etap:is(
test_same_suffix(100, Suffix),
true,
"utc_id ids should have the same suffix."
)
end,
run_test([default_config(), utc_id_alg_config()], TestSuffix).

test_unique(0, _) ->
true;
Expand All @@ -116,3 +132,16 @@ gen_until_pref_change(Prefix, N) ->
Prefix -> gen_until_pref_change(Prefix, N+1);
_ -> N
end.

get_suffix(UUID) when is_binary(UUID)->
get_suffix(binary_to_list(UUID));
get_suffix(UUID) ->
element(2, lists:split(14, UUID)).

test_same_suffix(0, _) ->
true;
test_same_suffix(N, Suffix) ->
case get_suffix(couch_uuids:new()) of
Suffix -> test_same_suffix(N-1, Suffix);
_ -> false
end.

0 comments on commit 5ab712a

Please sign in to comment.