xref: /2.1.1/geocouch/test/200-compact.t (revision b89b0294)
1#!/usr/bin/env escript
2%% -*- erlang -*-
3
4% Licensed under the Apache License, Version 2.0 (the "License"); you may not
5% use this file except in compliance with the License. You may obtain a copy of
6% the License at
7%
8%   http://www.apache.org/licenses/LICENSE-2.0
9%
10% Unless required by applicable law or agreed to in writing, software
11% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13% License for the specific language governing permissions and limitations under
14% the License.
15
16-record(user_ctx, {
17    name = null,
18    roles = [],
19    handler
20}).
21
22test_db1_name() -> <<"geocouch_test_compaction">>.
23test_db2_name() -> <<"geocouch_test_compaction_foreign">>.
24ddoc_name() -> <<"foo">>.
25admin_user_ctx() -> {user_ctx, #user_ctx{roles = [<<"_admin">>]}}.
26
27main(_) ->
28    code:add_pathz(filename:dirname(escript:script_name())),
29    gc_test_util:init_code_path(),
30    etap:plan(6),
31    case (catch test()) of
32        ok ->
33            etap:end_tests();
34        Other ->
35            etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),
36            etap:bail(Other)
37    end,
38    ok.
39
40test() ->
41    ok = ssl:start(),
42    ok = lhttpc:start(),
43    GeoCouchConfig = filename:join(
44        gc_test_util:root_dir() ++ [gc_test_util:gc_config_file()]),
45    ConfigFiles = test_util:config_files() ++ [GeoCouchConfig],
46    couch_server_sup:start_link(ConfigFiles),
47    timer:sleep(1000),
48    put(addr, couch_config:get("httpd", "bind_address", "127.0.0.1")),
49    put(port, integer_to_list(mochiweb_socket_server:get(couch_httpd, port))),
50
51    delete_dbs(),
52    create_dbs(),
53    add_design_doc(test_db1_name()),
54
55    test_compaction(),
56    test_compaction_foreign(),
57
58    delete_dbs(),
59    couch_server_sup:stop(),
60    ok.
61
62
63test_compaction() ->
64    DbName = test_db1_name(),
65    DdocName = <<"_design/", (ddoc_name())/binary>>,
66
67    insert(DbName),
68    query_spatial(DbName),
69    insert(DbName),
70    query_spatial(DbName),
71    insert(DbName),
72    query_spatial(DbName),
73
74    SpatialGroup = couch_spatial:get_group_server(DbName, DdocName),
75    etap:is(is_pid(SpatialGroup), true, "got spatial group pid"),
76    etap:is(is_process_alive(SpatialGroup), true,
77        "spatial group pid is alive"),
78
79    PreCompact = get_spatial_size(DbName),
80    compact_spatial_group(DbName, ddoc_name()),
81    PostCompact = get_spatial_size(DbName),
82
83    etap:is(PostCompact < PreCompact, true, "spatial view got compacted"),
84    ok.
85
86% This test tests compaction with a Design Document which is not in the same
87% database as the data
88test_compaction_foreign() ->
89    DbName = test_db2_name(),
90    DdocName = <<"_design/", (ddoc_name())/binary>>,
91    % Name of the database that holds the Design Document
92    DdocDbName = test_db1_name(),
93
94    insert(DbName),
95    query_spatial(DbName, DdocDbName),
96    insert(DbName),
97    query_spatial(DbName, DdocDbName),
98    insert(DbName),
99    query_spatial(DbName, DdocDbName),
100
101
102    SpatialGroup = couch_spatial:get_group_server(
103        {DbName, DdocDbName}, DdocName),
104    etap:is(is_pid(SpatialGroup), true, "got spatial group pid (b)"),
105    etap:is(is_process_alive(SpatialGroup), true,
106        "spatial group pid is alive (b)"),
107
108    PreCompact = get_spatial_size({DbName, DdocDbName}),
109    compact_spatial_group({DbName, DdocDbName}, ddoc_name()),
110    PostCompact = get_spatial_size({DbName, DdocDbName}),
111
112    etap:is(PostCompact < PreCompact, true, "spatial view got compacted (b)"),
113    ok.
114
115
116create_dbs() ->
117    {ok, Db1} = couch_db:create(test_db1_name(), [admin_user_ctx()]),
118    {ok, Db2} = couch_db:create(test_db2_name(), [admin_user_ctx()]),
119    couch_db:close(Db1),
120    couch_db:close(Db2),
121    ok.
122
123delete_dbs() ->
124    couch_server:delete(test_db1_name(), [admin_user_ctx()]),
125    couch_server:delete(test_db2_name(), [admin_user_ctx()]).
126
127
128compact_spatial_group(DbName, DdocName) ->
129    ok = couch_spatial_compactor:start_compact(DbName, DdocName),
130    wait_compaction_finished(DbName).
131
132
133add_design_doc(DbName) ->
134    {ok, Db} = couch_db:open_int(DbName, [admin_user_ctx()]),
135    DDoc = couch_doc:from_json_obj({[
136        {<<"meta">>, {[
137            {<<"id">>, <<"_design/foo">>}]}},
138        {<<"json">>, {[
139            {<<"language">>, <<"javascript">>},
140            {<<"spatial">>, {[
141                {<<"foo">>, <<"function(doc) { emit({type: \"Point\", coordinates: [0,0]}, doc); }">>}
142            ]}}
143        ]}}
144    ]}),
145    ok = couch_db:update_docs(Db, [DDoc]),
146    {ok, _} = couch_db:ensure_full_commit(Db),
147    couch_db:close(Db),
148    ok.
149
150
151% Inserts documents and queries the spatial view
152insert(DbName) ->
153    {ok, Db} = couch_db:open_int(DbName, [admin_user_ctx()]),
154    _Docs = lists:map(
155        fun(_) ->
156            Doc = couch_doc:from_json_obj(
157                {[{<<"meta">>, {[{<<"id">>, couch_uuids:new()}]}}]}),
158            ok = couch_db:update_docs(Db, [Doc])
159        end,
160        lists:seq(1, 100)),
161    couch_db:close(Db),
162    ok.
163
164query_spatial(DbName) ->
165    {ok, Db} = couch_db:open_int(DbName, [admin_user_ctx()]),
166    DdocName = <<"_design/", (ddoc_name())/binary>>,
167    % Don't use the HTTP API, as it doesn't support foreig Design
168    % Documents (those are Design Documents that are not in the same
169    % database as the data is).
170    {ok, _Index, _Group} = couch_spatial:get_spatial_index(
171        Db, DdocName, ddoc_name(), nil),
172    couch_db:close(Db).
173
174query_spatial(DbName, DdocDbName) ->
175    {ok, Db} = couch_db:open_int(DbName, [admin_user_ctx()]),
176    {ok, DdocDb} = couch_db:open_int(DdocDbName, [admin_user_ctx()]),
177    DdocName = <<"_design/", (ddoc_name())/binary>>,
178    % Don't use the HTTP API, as it doesn't support foreig Design
179    % Documents (those are Design Documents that are not in the same
180    % database as the data is).
181    {ok, _Index, _Group} = couch_spatial:get_spatial_index(
182        Db, {DdocDb, DdocName}, ddoc_name(), nil),
183    couch_db:close(Db),
184    couch_db:close(DdocDb).
185
186
187wait_compaction_finished(DbName) ->
188    Parent = self(),
189    Loop = spawn_link(fun() -> wait_loop(Parent, DbName) end),
190    receive
191    {done, Loop} ->
192        etap:diag("Spatial compaction has finished")
193    after 60000 ->
194        etap:bail("Compaction not triggered")
195    end.
196
197wait_loop(Parent, DbName) ->
198    DdocName = <<"_design/", (ddoc_name())/binary>>,
199    {ok, SpatialInfo} = couch_spatial:get_group_info(DbName, DdocName),
200    case couch_util:get_value(compact_running, SpatialInfo) =:= true of
201    false ->
202        Parent ! {done, self()};
203    true ->
204        ok = timer:sleep(500),
205        wait_loop(Parent, DbName)
206    end.
207
208
209get_spatial_size(DbName) ->
210    DdocName = <<"_design/", (ddoc_name())/binary>>,
211    {ok, SpatialInfo} = couch_spatial:get_group_info(DbName, DdocName),
212    couch_util:get_value(disk_size, SpatialInfo).
213