summaryrefslogtreecommitdiff
path: root/test/elixir/test/partition_size_limit_test.exs
diff options
context:
space:
mode:
Diffstat (limited to 'test/elixir/test/partition_size_limit_test.exs')
-rw-r--r--test/elixir/test/partition_size_limit_test.exs305
1 files changed, 305 insertions, 0 deletions
diff --git a/test/elixir/test/partition_size_limit_test.exs b/test/elixir/test/partition_size_limit_test.exs
new file mode 100644
index 000000000..b4be6480e
--- /dev/null
+++ b/test/elixir/test/partition_size_limit_test.exs
@@ -0,0 +1,305 @@
+defmodule PartitionSizeLimitTest do
+ use CouchTestCase
+
+ @moduledoc """
+ Test Partition size limit functionality
+ """
+
+ @max_size 10_240
+
+ setup do
+ db_name = random_db_name()
+ {:ok, _} = create_db(db_name, query: %{partitioned: true, q: 1})
+ on_exit(fn -> delete_db(db_name) end)
+
+ set_config({"couchdb", "max_partition_size", Integer.to_string(@max_size)})
+
+ {:ok, [db_name: db_name]}
+ end
+
+ defp get_db_info(dbname) do
+ resp = Couch.get("/#{dbname}")
+ assert resp.status_code == 200
+ %{:body => body} = resp
+ body
+ end
+
+ defp get_partition_info(dbname, partition) do
+ resp = Couch.get("/#{dbname}/_partition/#{partition}")
+ assert resp.status_code == 200
+ %{:body => body} = resp
+ body
+ end
+
+ defp open_doc(db_name, docid, status_assert \\ 200) do
+ resp = Couch.get("/#{db_name}/#{docid}")
+ assert resp.status_code == status_assert
+ %{:body => body} = resp
+ body
+ end
+
+ defp save_doc(db_name, doc, status_assert \\ 201) do
+ resp = Couch.post("/#{db_name}", query: [w: 3], body: doc)
+ assert resp.status_code == status_assert
+ %{:body => body} = resp
+ body["rev"]
+ end
+
+ defp delete_doc(db_name, doc, status_assert \\ 200) do
+ url = "/#{db_name}/#{doc["_id"]}"
+ rev = doc["_rev"]
+ resp = Couch.delete(url, query: [w: 3, rev: rev])
+ assert resp.status_code == status_assert
+ %{:body => body} = resp
+ body["rev"]
+ end
+
+ defp fill_partition(db_name, partition \\ "foo") do
+ docs =
+ 1..15
+ |> Enum.map(fn i ->
+ id = i |> Integer.to_string() |> String.pad_leading(4, "0")
+ docid = "#{partition}:#{id}"
+ %{_id: docid, value: "0" |> String.pad_leading(1024)}
+ end)
+
+ body = %{:w => 3, :docs => docs}
+ resp = Couch.post("/#{db_name}/_bulk_docs", body: body)
+ assert resp.status_code == 201
+ end
+
+ defp compact(db) do
+ assert Couch.post("/#{db}/_compact").status_code == 202
+
+ retry_until(
+ fn ->
+ Couch.get("/#{db}").body["compact_running"] == false
+ end,
+ 200,
+ 20_000
+ )
+ end
+
+ test "fill partition manually", context do
+ db_name = context[:db_name]
+ partition = "foo"
+
+ resp =
+ 1..1000
+ |> Enum.find_value(0, fn i ->
+ id = i |> Integer.to_string() |> String.pad_leading(4, "0")
+ docid = "#{partition}:#{id}"
+ doc = %{_id: docid, value: "0" |> String.pad_leading(1024)}
+ resp = Couch.post("/#{db_name}", query: [w: 3], body: doc)
+
+ if resp.status_code == 201 do
+ false
+ else
+ resp
+ end
+ end)
+
+ assert resp.status_code == 403
+ %{body: body} = resp
+ assert body["error"] == "partition_overflow"
+
+ info = get_partition_info(db_name, partition)
+ assert info["sizes"]["external"] >= @max_size
+ end
+
+ test "full partitions reject POST /dbname", context do
+ db_name = context[:db_name]
+ fill_partition(db_name)
+
+ doc = %{_id: "foo:bar", value: "stuff"}
+ resp = Couch.post("/#{db_name}", query: [w: 3], body: doc)
+ assert resp.status_code == 403
+ %{body: body} = resp
+ assert body["error"] == "partition_overflow"
+ end
+
+ test "full partitions reject PUT /dbname/docid", context do
+ db_name = context[:db_name]
+ fill_partition(db_name)
+
+ doc = %{value: "stuff"}
+ resp = Couch.put("/#{db_name}/foo:bar", query: [w: 3], body: doc)
+ assert resp.status_code == 403
+ %{body: body} = resp
+ assert body["error"] == "partition_overflow"
+ end
+
+ test "full partitions reject POST /dbname/_bulk_docs", context do
+ db_name = context[:db_name]
+ fill_partition(db_name)
+
+ body = %{w: 3, docs: [%{_id: "foo:bar"}]}
+ resp = Couch.post("/#{db_name}/_bulk_docs", query: [w: 3], body: body)
+ assert resp.status_code == 201
+ %{body: body} = resp
+ doc_resp = Enum.at(body, 0)
+ assert doc_resp["error"] == "partition_overflow"
+ end
+
+ test "full partitions with mixed POST /dbname/_bulk_docs", context do
+ db_name = context[:db_name]
+ fill_partition(db_name)
+
+ body = %{w: 3, docs: [%{_id: "foo:bar"}, %{_id: "baz:bang"}]}
+ resp = Couch.post("/#{db_name}/_bulk_docs", query: [w: 3], body: body)
+ assert resp.status_code == 201
+ %{body: body} = resp
+
+ doc_resp1 = Enum.at(body, 0)
+ assert doc_resp1["error"] == "partition_overflow"
+
+ doc_resp2 = Enum.at(body, 1)
+ assert doc_resp2["ok"]
+ end
+
+ test "full partitions are still readable", context do
+ db_name = context[:db_name]
+ fill_partition(db_name)
+ open_doc(db_name, "foo:0001")
+ end
+
+ test "full partitions can accept deletes", context do
+ db_name = context[:db_name]
+ fill_partition(db_name)
+
+ doc = open_doc(db_name, "foo:0001")
+ delete_doc(db_name, doc)
+ end
+
+ test "full partitions can accept updates that reduce size", context do
+ db_name = context[:db_name]
+ fill_partition(db_name)
+
+ doc = open_doc(db_name, "foo:0001")
+ save_doc(db_name, %{doc | "value" => ""})
+ end
+
+ test "full partition does not affect other partitions", context do
+ db_name = context[:db_name]
+ fill_partition(db_name)
+ save_doc(db_name, %{_id: "bar:foo", value: "stuff"})
+ end
+
+ test "full partition does not affect design documents", context do
+ db_name = context[:db_name]
+ fill_partition(db_name)
+ rev1 = save_doc(db_name, %{_id: "_design/foo", value: "stuff"})
+ save_doc(db_name, %{_id: "_design/foo", _rev: rev1, value: "hi"})
+ doc = open_doc(db_name, "_design/foo")
+ delete_doc(db_name, doc)
+ end
+
+ test "replication into a full partition works", context do
+ db_name = context[:db_name]
+ fill_partition(db_name)
+ save_doc(db_name, %{_id: "foo:bar", value: "stuff"}, 403)
+
+ doc = %{
+ _id: "foo:bar",
+ _rev: <<"1-23202479633c2b380f79507a776743d5">>,
+ value: "stuff"
+ }
+
+ url = "/#{db_name}/#{doc[:_id]}"
+ query = [new_edits: false, w: 3]
+ resp = Couch.put(url, query: query, body: doc)
+ assert resp.status_code == 201
+ end
+
+ test "compacting a full partition works", context do
+ db_name = context[:db_name]
+ db_info1 = get_db_info(db_name)
+ fill_partition(db_name)
+ compact(db_name)
+ db_info2 = get_db_info(db_name)
+ assert db_info2["sizes"]["file"] != db_info1["sizes"]["file"]
+ end
+
+ test "indexing a full partition works", context do
+ db_name = context[:db_name]
+ fill_partition(db_name)
+
+ ddoc = %{
+ _id: "_design/foo",
+ views: %{
+ bar: %{
+ map: "function(doc) {emit(doc.group, 1);}"
+ }
+ }
+ }
+
+ save_doc(db_name, ddoc)
+
+ url = "/#{db_name}/_partition/foo/_design/foo/_view/bar"
+ resp = Couch.get(url)
+ assert resp.status_code == 200
+ %{body: body} = resp
+
+ assert length(body["rows"]) > 0
+ end
+
+ test "purging docs allows writes", context do
+ db_name = context[:db_name]
+ fill_partition(db_name)
+
+ info = get_partition_info(db_name, "foo")
+ limit = info["doc_count"] - 1
+
+ query = [
+ start_key: "\"foo:0000\"",
+ end_key: "\"foo:9999\"",
+ limit: limit
+ ]
+
+ resp = Couch.get("/#{db_name}/_all_docs", query: query)
+ assert resp.status_code == 200
+ %{body: body} = resp
+
+ pbody =
+ body["rows"]
+ |> Enum.reduce(%{}, fn row, acc ->
+ Map.put(acc, row["id"], [row["value"]["rev"]])
+ end)
+
+ resp = Couch.post("/#{db_name}/_purge", query: [w: 3], body: pbody)
+ assert resp.status_code == 201
+
+ save_doc(db_name, %{_id: "foo:bar", value: "some value"})
+ end
+
+ test "increasing partition size allows more writes", context do
+ db_name = context[:db_name]
+ fill_partition(db_name)
+
+ # We use set_config_raw so that we're not setting
+ # on_exit handlers that might interfere with the original
+ # config change done in setup of this test
+ new_size = Integer.to_string(@max_size * 1000)
+ set_config_raw("couchdb", "max_partition_size", new_size)
+
+ save_doc(db_name, %{_id: "foo:bar", value: "stuff"})
+ end
+
+ test "decreasing partition size disables more writes", context do
+ db_name = context[:db_name]
+
+ # We use set_config_raw so that we're not setting
+ # on_exit handlers that might interfere with the original
+ # config change done in setup of this test
+ new_size = Integer.to_string(@max_size * 1000)
+ set_config_raw("couchdb", "max_partition_size", new_size)
+
+ fill_partition(db_name)
+ save_doc(db_name, %{_id: "foo:bar", value: "stuff"})
+
+ old_size = Integer.to_string(@max_size)
+ set_config_raw("couchdb", "max_partition_size", old_size)
+
+ save_doc(db_name, %{_id: "foo:baz", value: "stuff"}, 403)
+ end
+end