You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2019/05/02 14:42:24 UTC

[couchdb] branch prototype/rfc-001-revision-metadata-model updated (64c3a84 -> 44bc553)

This is an automated email from the ASF dual-hosted git repository.

davisp pushed a change to branch prototype/rfc-001-revision-metadata-model
in repository https://gitbox.apache.org/repos/asf/couchdb.git.


    from 64c3a84  Add tests for fold_changes
     new a9efd04  Tests
     new 44bc553  Pass a TxDb to fold functions

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/fabric/src/fabric2_db.erl                      | 18 +++--
 src/fabric/src/fabric2_fdb.erl                     | 61 ++++++++++-------
 src/fabric/src/fabric2_txids.erl                   |  9 ++-
 src/fabric/test/fabric2_changes_fold_tests.erl     | 18 ++---
 src/fabric/test/fabric2_doc_fold_tests.erl         | 22 +++---
 src/fabric/test/fabric2_fdb_tx_retry_tests.erl     |  4 +-
 src/fabric/test/fabric2_trace_doc_create_tests.erl | 79 ++++++++++++++++++++++
 7 files changed, 154 insertions(+), 57 deletions(-)
 create mode 100644 src/fabric/test/fabric2_trace_doc_create_tests.erl


[couchdb] 02/02: Pass a TxDb to fold functions

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

davisp pushed a commit to branch prototype/rfc-001-revision-metadata-model
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 44bc5533ae1928c5e174065c0cf9b14113a5fd63
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Thu May 2 09:42:06 2019 -0500

    Pass a TxDb to fold functions
---
 src/fabric/src/fabric2_fdb.erl                     | 17 ++++++++++-------
 src/fabric/test/fabric2_changes_fold_tests.erl     | 18 +++++++++---------
 src/fabric/test/fabric2_doc_fold_tests.erl         | 22 +++++++++++-----------
 src/fabric/test/fabric2_fdb_tx_retry_tests.erl     |  4 ++--
 src/fabric/test/fabric2_trace_doc_create_tests.erl |  3 +++
 5 files changed, 35 insertions(+), 29 deletions(-)

diff --git a/src/fabric/src/fabric2_fdb.erl b/src/fabric/src/fabric2_fdb.erl
index 3666268..acb8b82 100644
--- a/src/fabric/src/fabric2_fdb.erl
+++ b/src/fabric/src/fabric2_fdb.erl
@@ -80,7 +80,10 @@ do_transaction(Fun, LayerPrefix) when is_function(Fun, 1) ->
     Db = get_db_handle(),
     try
         erlfdb:transactional(Db, fun(Tx) ->
-            erlfdb:set_option(Tx, transaction_logging_enable),
+            case get('erlfdb_trace') of
+                true -> erlfdb:set_option(Tx, transaction_logging_enable);
+                _ -> ok
+            end,
             case is_transaction_applied(Tx) of
                 true ->
                     get_previous_transaction_result();
@@ -567,7 +570,7 @@ fold_docs(#{} = Db, UserFun, UserAcc0, Options) ->
     DocCountBin = erlfdb:wait(erlfdb:get(Tx, DocCountKey)),
 
     try
-        UserAcc1 = maybe_stop(UserFun({meta, [
+        UserAcc1 = maybe_stop(UserFun(Db, {meta, [
             {total, ?bin2uint(DocCountBin)},
             {offset, null}
         ]}, UserAcc0)),
@@ -575,14 +578,14 @@ fold_docs(#{} = Db, UserFun, UserAcc0, Options) ->
         UserAcc2 = erlfdb:fold_range(Tx, Start, End, fun({K, V}, UserAccIn) ->
             {?DB_ALL_DOCS, DocId} = erlfdb_tuple:unpack(K, DbPrefix),
             RevId = erlfdb_tuple:unpack(V),
-            maybe_stop(UserFun({row, [
+            maybe_stop(UserFun(Db, {row, [
                 {id, DocId},
                 {key, DocId},
                 {value, couch_doc:rev_to_str(RevId)}
             ]}, UserAccIn))
         end, UserAcc1, [{reverse, Reverse}]),
 
-        {ok, maybe_stop(UserFun(complete, UserAcc2))}
+        {ok, maybe_stop(UserFun(Db, complete, UserAcc2))}
     catch throw:{stop, FinalUserAcc} ->
         {ok, FinalUserAcc}
     end.
@@ -619,7 +622,7 @@ fold_changes(#{} = Db, SinceSeq0, UserFun, UserAcc0, Options) ->
         <<51:8, FirstSeq:12/binary>> = erlfdb_tuple:pack({SinceSeq1}),
         put('$last_changes_seq', fabric2_util:to_hex(FirstSeq)),
 
-        UserAcc1 = maybe_stop(UserFun(start, UserAcc0)),
+        UserAcc1 = maybe_stop(UserFun(Db, start, UserAcc0)),
 
         UserAcc2 = erlfdb:fold_range(Tx, Start, End, fun({K, V}, UserAccIn) ->
             {?DB_CHANGES, UpdateSeq} = erlfdb_tuple:unpack(K, DbPrefix),
@@ -635,14 +638,14 @@ fold_changes(#{} = Db, SinceSeq0, UserFun, UserAcc0, Options) ->
                 [{deleted, true}]
             end,
 
-            maybe_stop(UserFun({change, {[
+            maybe_stop(UserFun(Db, {change, {[
                 {seq, SeqHex},
                 {id, DocId},
                 {changes, [{[{rev, couch_doc:rev_to_str(RevId)}]}]}
             ] ++ DelMember}}, UserAccIn))
         end, UserAcc1, [{reverse, Reverse}]),
 
-        UserFun({stop, get('$last_changes_seq'), null}, UserAcc2)
+        UserFun(Db, {stop, get('$last_changes_seq'), null}, UserAcc2)
     catch throw:{stop, FinalUserAcc} ->
         {ok, FinalUserAcc}
     after
diff --git a/src/fabric/test/fabric2_changes_fold_tests.erl b/src/fabric/test/fabric2_changes_fold_tests.erl
index 20f57a4..a8b3b1f 100644
--- a/src/fabric/test/fabric2_changes_fold_tests.erl
+++ b/src/fabric/test/fabric2_changes_fold_tests.erl
@@ -67,12 +67,12 @@ cleanup({Db, _DocIdRevs, Ctx}) ->
 
 
 fold_changes_basic({Db, DocRows, _}) ->
-    {ok, Rows} = fabric2_db:fold_changes(Db, 0, fun fold_fun/2, []),
+    {ok, Rows} = fabric2_db:fold_changes(Db, 0, fun fold_fun/3, []),
     ?assertEqual(lists:reverse(DocRows), Rows).
 
 
 fold_changes_since_now({Db, _, _}) ->
-    {ok, Rows} = fabric2_db:fold_changes(Db, now, fun fold_fun/2, []),
+    {ok, Rows} = fabric2_db:fold_changes(Db, now, fun fold_fun/3, []),
     ?assertEqual([], Rows).
 
 
@@ -81,20 +81,20 @@ fold_changes_since_seq({_, [], _}) ->
 
 fold_changes_since_seq({Db, [Row | RestRows], _}) ->
     #{seq := Since} = Row,
-    {ok, Rows} = fabric2_db:fold_changes(Db, Since, fun fold_fun/2, []),
+    {ok, Rows} = fabric2_db:fold_changes(Db, Since, fun fold_fun/3, []),
     ?assertEqual(lists:reverse(RestRows), Rows),
     fold_changes_since_seq({Db, RestRows, nil}).
 
 
 fold_changes_basic_rev({Db, _, _}) ->
     Opts = [{dir, rev}],
-    {ok, Rows} = fabric2_db:fold_changes(Db, 0, fun fold_fun/2, [], Opts),
+    {ok, Rows} = fabric2_db:fold_changes(Db, 0, fun fold_fun/3, [], Opts),
     ?assertEqual([], Rows).
 
 
 fold_changes_since_now_rev({Db, DocRows, _}) ->
     Opts = [{dir, rev}],
-    {ok, Rows} = fabric2_db:fold_changes(Db, now, fun fold_fun/2, [], Opts),
+    {ok, Rows} = fabric2_db:fold_changes(Db, now, fun fold_fun/3, [], Opts),
     ?assertEqual(DocRows, Rows).
 
 
@@ -104,15 +104,15 @@ fold_changes_since_seq_rev({_, [], _}) ->
 fold_changes_since_seq_rev({Db, DocRows, _}) ->
     #{seq := Since} = lists:last(DocRows),
     Opts = [{dir, rev}],
-    {ok, Rows} = fabric2_db:fold_changes(Db, Since, fun fold_fun/2, [], Opts),
+    {ok, Rows} = fabric2_db:fold_changes(Db, Since, fun fold_fun/3, [], Opts),
     ?assertEqual(DocRows, Rows),
     RestRows = lists:sublist(DocRows, length(DocRows) - 1),
     fold_changes_since_seq_rev({Db, RestRows, nil}).
 
 
-fold_fun(start, Acc) ->
+fold_fun(_Db, start, Acc) ->
     {ok, Acc};
-fold_fun({change, {Props}}, Acc) ->
+fold_fun(_Db, {change, {Props}}, Acc) ->
     [{[{rev, Rev}]}] = fabric2_util:get_value(changes, Props),
     Row = #{
         id => fabric2_util:get_value(id, Props),
@@ -121,5 +121,5 @@ fold_fun({change, {Props}}, Acc) ->
         rev => Rev
     },
     {ok, [Row | Acc]};
-fold_fun({stop, _LastSeq, null}, Acc) ->
+fold_fun(_Db, {stop, _LastSeq, null}, Acc) ->
     {ok, Acc}.
diff --git a/src/fabric/test/fabric2_doc_fold_tests.erl b/src/fabric/test/fabric2_doc_fold_tests.erl
index caa5f92..1ded7ba 100644
--- a/src/fabric/test/fabric2_doc_fold_tests.erl
+++ b/src/fabric/test/fabric2_doc_fold_tests.erl
@@ -61,14 +61,14 @@ cleanup({Db, _DocIdRevs, Ctx}) ->
 
 
 fold_docs_basic({Db, DocIdRevs, _}) ->
-    {ok, {?DOC_COUNT, Rows}} = fabric2_db:fold_docs(Db, fun fold_fun/2, []),
+    {ok, {?DOC_COUNT, Rows}} = fabric2_db:fold_docs(Db, fun fold_fun/3, []),
     ?assertEqual(DocIdRevs, lists:reverse(Rows)).
 
 
 fold_docs_rev({Db, DocIdRevs, _}) ->
     Opts = [{dir, rev}],
     {ok, {?DOC_COUNT, Rows}} =
-            fabric2_db:fold_docs(Db, fun fold_fun/2, [], Opts),
+            fabric2_db:fold_docs(Db, fun fold_fun/3, [], Opts),
     ?assertEqual(DocIdRevs, Rows).
 
 
@@ -76,7 +76,7 @@ fold_docs_with_start_key({Db, DocIdRevs, _}) ->
     {StartKey, _} = hd(DocIdRevs),
     Opts = [{start_key, StartKey}],
     {ok, {?DOC_COUNT, Rows}}
-            = fabric2_db:fold_docs(Db, fun fold_fun/2, [], Opts),
+            = fabric2_db:fold_docs(Db, fun fold_fun/3, [], Opts),
     ?assertEqual(DocIdRevs, lists:reverse(Rows)),
     if length(DocIdRevs) == 1 -> ok; true ->
         fold_docs_with_start_key({Db, tl(DocIdRevs), nil})
@@ -88,7 +88,7 @@ fold_docs_with_end_key({Db, DocIdRevs, _}) ->
     {EndKey, _} = hd(RevDocIdRevs),
     Opts = [{end_key, EndKey}],
     {ok, {?DOC_COUNT, Rows}} =
-            fabric2_db:fold_docs(Db, fun fold_fun/2, [], Opts),
+            fabric2_db:fold_docs(Db, fun fold_fun/3, [], Opts),
     ?assertEqual(RevDocIdRevs, Rows),
     if length(DocIdRevs) == 1 -> ok; true ->
         fold_docs_with_end_key({Db, lists:reverse(tl(RevDocIdRevs)), nil})
@@ -111,12 +111,12 @@ fold_docs_with_different_keys({Db, DocIdRevs, _}) ->
 check_all_combos(Db, StartKey, EndKey, Rows) ->
     Opts1 = make_opts(fwd, StartKey, EndKey, true),
     {ok, {?DOC_COUNT, Rows1}} =
-            fabric2_db:fold_docs(Db, fun fold_fun/2, [], Opts1),
+            fabric2_db:fold_docs(Db, fun fold_fun/3, [], Opts1),
     ?assertEqual(lists:reverse(Rows), Rows1),
 
     Opts2 = make_opts(fwd, StartKey, EndKey, false),
     {ok, {?DOC_COUNT, Rows2}} =
-            fabric2_db:fold_docs(Db, fun fold_fun/2, [], Opts2),
+            fabric2_db:fold_docs(Db, fun fold_fun/3, [], Opts2),
     Expect2 = if EndKey == undefined -> lists:reverse(Rows); true ->
         lists:reverse(all_but_last(Rows))
     end,
@@ -124,12 +124,12 @@ check_all_combos(Db, StartKey, EndKey, Rows) ->
 
     Opts3 = make_opts(rev, StartKey, EndKey, true),
     {ok, {?DOC_COUNT, Rows3}} =
-            fabric2_db:fold_docs(Db, fun fold_fun/2, [], Opts3),
+            fabric2_db:fold_docs(Db, fun fold_fun/3, [], Opts3),
     ?assertEqual(Rows, Rows3),
 
     Opts4 = make_opts(rev, StartKey, EndKey, false),
     {ok, {?DOC_COUNT, Rows4}} =
-            fabric2_db:fold_docs(Db, fun fold_fun/2, [], Opts4),
+            fabric2_db:fold_docs(Db, fun fold_fun/3, [], Opts4),
     Expect4 = if StartKey == undefined -> Rows; true ->
         tl(Rows)
     end,
@@ -197,13 +197,13 @@ pick_end_key(Rows) ->
     end.
 
 
-fold_fun({meta, Meta}, _Acc) ->
+fold_fun(_Db, {meta, Meta}, _Acc) ->
     Total = fabric2_util:get_value(total, Meta),
     {ok, {Total, []}};
-fold_fun({row, Row}, {Total, Rows}) ->
+fold_fun(_Db, {row, Row}, {Total, Rows}) ->
     RowId = fabric2_util:get_value(id, Row),
     RowId = fabric2_util:get_value(key, Row),
     RowRev = fabric2_util:get_value(value, Row),
     {ok, {Total, [{RowId, RowRev} | Rows]}};
-fold_fun(complete, Acc) ->
+fold_fun(_Db, complete, Acc) ->
     {ok, Acc}.
diff --git a/src/fabric/test/fabric2_fdb_tx_retry_tests.erl b/src/fabric/test/fabric2_fdb_tx_retry_tests.erl
index bfb6d25..c924ce5 100644
--- a/src/fabric/test/fabric2_fdb_tx_retry_tests.erl
+++ b/src/fabric/test/fabric2_fdb_tx_retry_tests.erl
@@ -100,7 +100,7 @@ run_on_first_try() ->
     meck:expect(erlfdb, get_last_error, fun() -> undefined end),
     meck:expect(erlfdb, clear, fun(_, _) -> ok end),
     meck:expect(erlfdb, is_read_only, fun(_) -> false end),
-    meck:expect(fabric2_txids, create, fun(_) -> <<"a txid">> end),
+    meck:expect(fabric2_txids, create, fun(_, _) -> <<"a txid">> end),
     meck:expect(erlfdb, set, fun(_, <<"a txid">>, <<>>) -> ok end),
     meck:expect(fabric2_txids, remove, fun(<<"a txid">>) -> ok end),
 
@@ -120,7 +120,7 @@ retry_when_commit_conflict() ->
     meck:expect(erlfdb, get_last_error, fun() -> 1020 end),
     meck:expect(erlfdb, clear, fun(_, _) -> ok end),
     meck:expect(erlfdb, is_read_only, fun(_) -> false end),
-    meck:expect(fabric2_txids, create, fun(_) -> <<"a txid">> end),
+    meck:expect(fabric2_txids, create, fun(_, _) -> <<"a txid">> end),
     meck:expect(erlfdb, set, fun(_, <<"a txid">>, <<>>) -> ok end),
     meck:expect(fabric2_txids, remove, fun(<<"a txid">>) -> ok end),
 
diff --git a/src/fabric/test/fabric2_trace_doc_create_tests.erl b/src/fabric/test/fabric2_trace_doc_create_tests.erl
index 21b9738..279533b 100644
--- a/src/fabric/test/fabric2_trace_doc_create_tests.erl
+++ b/src/fabric/test/fabric2_trace_doc_create_tests.erl
@@ -46,6 +46,7 @@ cleanup({Db, Ctx}) ->
 
 
 create_new_doc({Db, _}) ->
+    put(erlfdb_trace, true),
     Doc = #doc{
         id = fabric2_util:uuid(),
         body = {[{<<"foo">>, <<"bar">>}]}
@@ -54,6 +55,7 @@ create_new_doc({Db, _}) ->
 
 
 create_two_docs({Db, _}) ->
+    put(erlfdb_trace, true),
     Doc1 = #doc{
         id = fabric2_util:uuid(),
         body = {[{<<"bam">>, <<"baz">>}]}
@@ -66,6 +68,7 @@ create_two_docs({Db, _}) ->
 
 
 create_50_docs({Db, _}) ->
+    put(erlfdb_trace, true),
     Docs = lists:map(fun(Val) ->
         #doc{
             id = fabric2_util:uuid(),


[couchdb] 01/02: Tests

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

davisp pushed a commit to branch prototype/rfc-001-revision-metadata-model
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit a9efd04d6d65979b36922e73d0737237e3c96f50
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Thu May 2 08:36:04 2019 -0500

    Tests
---
 src/fabric/src/fabric2_db.erl                      | 18 +++--
 src/fabric/src/fabric2_fdb.erl                     | 46 +++++++------
 src/fabric/src/fabric2_txids.erl                   |  9 ++-
 src/fabric/test/fabric2_trace_doc_create_tests.erl | 76 ++++++++++++++++++++++
 4 files changed, 120 insertions(+), 29 deletions(-)

diff --git a/src/fabric/src/fabric2_db.erl b/src/fabric/src/fabric2_db.erl
index c276ae2..d5842e8 100644
--- a/src/fabric/src/fabric2_db.erl
+++ b/src/fabric/src/fabric2_db.erl
@@ -153,7 +153,7 @@ create(DbName, Options) ->
     case Result of
         #{} = Db ->
             ok = fabric2_server:store(Db),
-            {ok, Db#{tx => undefined}};
+            {ok, Db#{tx := undefined}};
         Error ->
             Error
     end.
@@ -164,11 +164,17 @@ open(DbName, Options) ->
         #{} = Db ->
             {ok, maybe_set_user_ctx(Db, Options)};
         undefined ->
-            fabric2_fdb:transactional(DbName, Options, fun(TxDb) ->
-                Opened = fabric2_fdb:open(TxDb, Options),
-                ok = fabric2_server:store(Opened),
-                {ok, Opened#{tx => undefined}}
-            end)
+            Result = fabric2_fdb:transactional(DbName, Options, fun(TxDb) ->
+                fabric2_fdb:open(TxDb, Options)
+            end),
+            % Cache outside the transaction retry loop
+            case Result of
+                #{} = Db ->
+                    ok = fabric2_server:store(Db),
+                    {ok, Db#{tx := undefined}};
+                Error ->
+                    Error
+            end
     end.
 
 
diff --git a/src/fabric/src/fabric2_fdb.erl b/src/fabric/src/fabric2_fdb.erl
index 769a5a8..3666268 100644
--- a/src/fabric/src/fabric2_fdb.erl
+++ b/src/fabric/src/fabric2_fdb.erl
@@ -56,20 +56,8 @@
 -include("fabric2.hrl").
 
 
-transactional(Fun) when is_function(Fun, 1) ->
-    Db = get_db_handle(),
-    try
-        erlfdb:transactional(Db, fun(Tx) ->
-            case is_transaction_applied(Tx) of
-                true ->
-                    get_previous_transaction_result();
-                false ->
-                    execute_transaction(Tx, Fun)
-            end
-        end)
-    after
-        clear_transaction()
-    end.
+transactional(Fun) ->
+    do_transaction(Fun, undefined).
 
 
 transactional(DbName, Options, Fun) when is_binary(DbName) ->
@@ -79,14 +67,32 @@ transactional(DbName, Options, Fun) when is_binary(DbName) ->
 
 
 transactional(#{tx := undefined} = Db, Fun) ->
-    transactional(fun(Tx) ->
+    #{layer_prefix := LayerPrefix} = Db,
+    do_transaction(fun(Tx) ->
         Fun(Db#{tx => Tx})
-    end);
+    end, LayerPrefix);
 
 transactional(#{tx := {erlfdb_transaction, _}} = Db, Fun) ->
     Fun(Db).
 
 
+do_transaction(Fun, LayerPrefix) when is_function(Fun, 1) ->
+    Db = get_db_handle(),
+    try
+        erlfdb:transactional(Db, fun(Tx) ->
+            erlfdb:set_option(Tx, transaction_logging_enable),
+            case is_transaction_applied(Tx) of
+                true ->
+                    get_previous_transaction_result();
+                false ->
+                    execute_transaction(Tx, Fun, LayerPrefix)
+            end
+        end)
+    after
+        clear_transaction()
+    end.
+
+
 create(#{} = Db0, Options) ->
     #{
         name := DbName,
@@ -936,13 +942,13 @@ get_previous_transaction_result() ->
     get(?PDICT_TX_RES_KEY).
 
 
-execute_transaction(Tx, Fun) ->
+execute_transaction(Tx, Fun, LayerPrefix) ->
     Result = Fun(Tx),
     case erlfdb:is_read_only(Tx) of
         true ->
             ok;
         false ->
-            erlfdb:set(Tx, get_transaction_id(Tx), <<>>),
+            erlfdb:set(Tx, get_transaction_id(Tx, LayerPrefix), <<>>),
             put(?PDICT_TX_RES_KEY, Result)
     end,
     Result.
@@ -966,10 +972,10 @@ transaction_id_exists(Tx) ->
     erlfdb:wait(erlfdb:get(Tx, get(?PDICT_TX_ID_KEY))) == <<>>.
 
 
-get_transaction_id(Tx) ->
+get_transaction_id(Tx, LayerPrefix) ->
     case get(?PDICT_TX_ID_KEY) of
         undefined ->
-            TxId = fabric2_txids:create(Tx),
+            TxId = fabric2_txids:create(Tx, LayerPrefix),
             put(?PDICT_TX_ID_KEY, TxId),
             TxId;
         TxId when is_binary(TxId) ->
diff --git a/src/fabric/src/fabric2_txids.erl b/src/fabric/src/fabric2_txids.erl
index a9d6390..bbb8bdf 100644
--- a/src/fabric/src/fabric2_txids.erl
+++ b/src/fabric/src/fabric2_txids.erl
@@ -17,7 +17,7 @@
 
 -export([
     start_link/0,
-    create/1,
+    create/2,
     remove/1
 ]).
 
@@ -43,13 +43,16 @@ start_link() ->
     gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
 
 
-create(Tx) ->
+create(Tx, undefined) ->
     Root = erlfdb_directory:root(),
     CouchDB = erlfdb_directory:create_or_open(Tx, Root, [<<"couchdb">>]),
     Prefix = erlfdb_directory:get_name(CouchDB),
+    create(Tx, Prefix);
+
+create(_Tx, LayerPrefix) ->
     {Mega, Secs, Micro} = os:timestamp(),
     Key = {?TX_IDS, Mega, Secs, Micro, fabric2_util:uuid()},
-    erlfdb_tuple:pack(Key, Prefix).
+    erlfdb_tuple:pack(Key, LayerPrefix).
 
 
 remove(TxId) when is_binary(TxId) ->
diff --git a/src/fabric/test/fabric2_trace_doc_create_tests.erl b/src/fabric/test/fabric2_trace_doc_create_tests.erl
new file mode 100644
index 0000000..21b9738
--- /dev/null
+++ b/src/fabric/test/fabric2_trace_doc_create_tests.erl
@@ -0,0 +1,76 @@
+% Licensed 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.
+
+-module(fabric2_trace_doc_create_tests).
+
+
+-include_lib("couch/include/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+doc_crud_test_() ->
+    {
+        "Test document CRUD operations",
+        {
+            setup,
+            fun setup/0,
+            fun cleanup/1,
+            {with, [
+                fun create_new_doc/1,
+                fun create_two_docs/1,
+                fun create_50_docs/1
+            ]}
+        }
+    }.
+
+
+setup() ->
+    Ctx = test_util:start_couch([fabric]),
+    {ok, Db} = fabric2_db:create(?tempdb(), [{user_ctx, ?ADMIN_USER}]),
+    {Db, Ctx}.
+
+
+cleanup({Db, Ctx}) ->
+    ok = fabric2_db:delete(fabric2_db:name(Db), []),
+    test_util:stop_couch(Ctx).
+
+
+create_new_doc({Db, _}) ->
+    Doc = #doc{
+        id = fabric2_util:uuid(),
+        body = {[{<<"foo">>, <<"bar">>}]}
+    },
+    {ok, _} = fabric2_db:update_doc(Db, Doc).
+
+
+create_two_docs({Db, _}) ->
+    Doc1 = #doc{
+        id = fabric2_util:uuid(),
+        body = {[{<<"bam">>, <<"baz">>}]}
+    },
+    Doc2 = #doc{
+        id = fabric2_util:uuid(),
+        body = {[{<<"bang">>, <<"bargle">>}]}
+    },
+    {ok, _} = fabric2_db:update_docs(Db, [Doc1, Doc2]).
+
+
+create_50_docs({Db, _}) ->
+    Docs = lists:map(fun(Val) ->
+        #doc{
+            id = fabric2_util:uuid(),
+            body = {[{<<"value">>, Val}]}
+        }
+    end, lists:seq(1, 50)),
+    {ok, _} = fabric2_db:update_docs(Db, Docs).
+