mirror of https://gitee.com/bigwinds/arangodb
1109 lines
41 KiB
Ruby
1109 lines
41 KiB
Ruby
# coding: utf-8
|
|
|
|
require 'rspec'
|
|
require 'json'
|
|
require 'arangodb.rb'
|
|
|
|
describe ArangoDB do
|
|
|
|
context "dealing with the replication interface:" do
|
|
|
|
################################################################################
|
|
## applier
|
|
################################################################################
|
|
|
|
context "dealing with the applier" do
|
|
|
|
api = "/_api/replication"
|
|
prefix = "api-replication"
|
|
|
|
before do
|
|
ArangoDB.put(api + "/applier-stop?global=true", :body => "")
|
|
ArangoDB.delete(api + "/applier-state?global=true", :body => "")
|
|
end
|
|
|
|
after do
|
|
ArangoDB.put(api + "/applier-stop?global=true", :body => "")
|
|
ArangoDB.delete(api + "/applier-state?global=true", :body => "")
|
|
end
|
|
|
|
################################################################################
|
|
## start
|
|
################################################################################
|
|
|
|
it "starts the applier" do
|
|
cmd = api + "/applier-start?global=true"
|
|
doc = ArangoDB.log_put("#{prefix}-applier-start", cmd, :body => "")
|
|
doc.code.should eq(400) # because configuration is invalid
|
|
end
|
|
|
|
################################################################################
|
|
## stop
|
|
################################################################################
|
|
|
|
it "stops the applier" do
|
|
cmd = api + "/applier-stop?global=true"
|
|
doc = ArangoDB.log_put("#{prefix}-applier-start", cmd, :body => "")
|
|
doc.code.should eq(200)
|
|
end
|
|
|
|
################################################################################
|
|
## properties
|
|
################################################################################
|
|
|
|
it "fetches the applier config" do
|
|
cmd = api + "/applier-config?global=true"
|
|
doc = ArangoDB.log_get("#{prefix}-applier-config", cmd, :body => "")
|
|
|
|
doc.code.should eq(200)
|
|
all = doc.parsed_response
|
|
all["requestTimeout"].should be_kind_of(Numeric)
|
|
all["connectTimeout"].should be_kind_of(Numeric)
|
|
all["ignoreErrors"].should be_kind_of(Integer)
|
|
all["maxConnectRetries"].should be_kind_of(Integer)
|
|
all["sslProtocol"].should be_kind_of(Integer)
|
|
all["chunkSize"].should be_kind_of(Integer)
|
|
all.should have_key("autoStart")
|
|
all.should have_key("adaptivePolling")
|
|
all.should have_key("autoResync")
|
|
all.should have_key("includeSystem")
|
|
all.should have_key("requireFromPresent")
|
|
all.should have_key("verbose")
|
|
all["restrictType"].should be_kind_of(String)
|
|
all["connectionRetryWaitTime"].should be_kind_of(Numeric)
|
|
all["initialSyncMaxWaitTime"].should be_kind_of(Numeric)
|
|
all["idleMinWaitTime"].should be_kind_of(Numeric)
|
|
all["idleMaxWaitTime"].should be_kind_of(Numeric)
|
|
end
|
|
|
|
################################################################################
|
|
## set and fetch properties
|
|
################################################################################
|
|
|
|
it "sets and re-fetches the applier config" do
|
|
cmd = api + "/applier-config?global=true"
|
|
body = '{ "endpoint" : "tcp://127.0.0.1:9999", "database" : "foo", "ignoreErrors" : 5, "requestTimeout" : 32.2, "connectTimeout" : 51.1, "maxConnectRetries" : 12345, "chunkSize" : 143423232, "autoStart" : true, "adaptivePolling" : false, "autoResync" : true, "includeSystem" : true, "requireFromPresent" : true, "verbose" : true, "connectionRetryWaitTime" : 22.12, "initialSyncMaxWaitTime" : 12.21, "idleMinWaitTime" : 1.4, "idleMaxWaitTime" : 7.3 }'
|
|
doc = ArangoDB.log_put("#{prefix}-applier-config", cmd, :body => body)
|
|
|
|
doc.code.should eq(200)
|
|
all = doc.parsed_response
|
|
all["endpoint"].should eq("tcp://127.0.0.1:9999")
|
|
all["database"].should eq("foo")
|
|
all["requestTimeout"].should eq(32.2)
|
|
all["connectTimeout"].should eq(51.1)
|
|
all["ignoreErrors"].should eq(5)
|
|
all["maxConnectRetries"].should eq(12345)
|
|
all["sslProtocol"].should eq(0)
|
|
all["chunkSize"].should eq(143423232)
|
|
all["autoStart"].should eq(true)
|
|
all["adaptivePolling"].should eq(false)
|
|
all["autoResync"].should eq(true)
|
|
all["includeSystem"].should eq(true)
|
|
all["requireFromPresent"].should eq(true)
|
|
all["verbose"].should eq(true)
|
|
all["connectionRetryWaitTime"].should eq(22.12)
|
|
all["initialSyncMaxWaitTime"].should eq(12.21)
|
|
all["idleMinWaitTime"].should eq(1.4)
|
|
all["idleMaxWaitTime"].should eq(7.3)
|
|
|
|
# refetch same data
|
|
doc = ArangoDB.log_get("#{prefix}-applier-config", cmd, :body => "")
|
|
|
|
doc.code.should eq(200)
|
|
all = doc.parsed_response
|
|
all["endpoint"].should eq("tcp://127.0.0.1:9999")
|
|
all["database"].should eq("foo")
|
|
all["requestTimeout"].should eq(32.2)
|
|
all["connectTimeout"].should eq(51.1)
|
|
all["ignoreErrors"].should eq(5)
|
|
all["maxConnectRetries"].should eq(12345)
|
|
all["sslProtocol"].should eq(0)
|
|
all["chunkSize"].should eq(143423232)
|
|
all["autoStart"].should eq(true)
|
|
all["adaptivePolling"].should eq(false)
|
|
all["autoResync"].should eq(true)
|
|
all["includeSystem"].should eq(true)
|
|
all["requireFromPresent"].should eq(true)
|
|
all["verbose"].should eq(true)
|
|
all["connectionRetryWaitTime"].should eq(22.12)
|
|
all["initialSyncMaxWaitTime"].should eq(12.21)
|
|
all["idleMinWaitTime"].should eq(1.4)
|
|
all["idleMaxWaitTime"].should eq(7.3)
|
|
|
|
|
|
body = '{ "endpoint" : "ssl://127.0.0.1:12345", "database" : "bar", "ignoreErrors" : 2, "requestTimeout" : 12.5, "connectTimeout" : 26.3, "maxConnectRetries" : 12, "chunkSize" : 1234567, "autoStart" : false, "adaptivePolling" : true, "autoResync" : false, "includeSystem" : false, "requireFromPresent" : false, "verbose" : false, "connectionRetryWaitTime" : 2.5, "initialSyncMaxWaitTime" : 4.3, "idleMinWaitTime" : 0.22, "idleMaxWaitTime" : 3.5 }'
|
|
doc = ArangoDB.log_put("#{prefix}-applier-config", cmd, :body => body)
|
|
|
|
doc.code.should eq(200)
|
|
all = doc.parsed_response
|
|
all["endpoint"].should eq("ssl://127.0.0.1:12345")
|
|
all["database"].should eq("bar")
|
|
all["requestTimeout"].should eq(12.5)
|
|
all["connectTimeout"].should eq(26.3)
|
|
all["ignoreErrors"].should eq(2)
|
|
all["maxConnectRetries"].should eq(12)
|
|
all["sslProtocol"].should eq(0)
|
|
all["chunkSize"].should eq(1234567)
|
|
all["autoStart"].should eq(false)
|
|
all["adaptivePolling"].should eq(true)
|
|
all["autoResync"].should eq(false)
|
|
all["includeSystem"].should eq(false)
|
|
all["requireFromPresent"].should eq(false)
|
|
all["verbose"].should eq(false)
|
|
all["connectionRetryWaitTime"].should eq(2.5)
|
|
all["initialSyncMaxWaitTime"].should eq(4.3)
|
|
all["idleMinWaitTime"].should eq(0.22)
|
|
all["idleMaxWaitTime"].should eq(3.5)
|
|
|
|
# refetch same data
|
|
doc = ArangoDB.log_get("#{prefix}-applier-config", cmd, :body => "")
|
|
|
|
doc.code.should eq(200)
|
|
all = doc.parsed_response
|
|
all["endpoint"].should eq("ssl://127.0.0.1:12345")
|
|
all["database"].should eq("bar")
|
|
all["requestTimeout"].should eq(12.5)
|
|
all["connectTimeout"].should eq(26.3)
|
|
all["ignoreErrors"].should eq(2)
|
|
all["maxConnectRetries"].should eq(12)
|
|
all["sslProtocol"].should eq(0)
|
|
all["chunkSize"].should eq(1234567)
|
|
all["autoStart"].should eq(false)
|
|
all["adaptivePolling"].should eq(true)
|
|
all["autoResync"].should eq(false)
|
|
all["includeSystem"].should eq(false)
|
|
all["requireFromPresent"].should eq(false)
|
|
all["verbose"].should eq(false)
|
|
all["connectionRetryWaitTime"].should eq(2.5)
|
|
all["initialSyncMaxWaitTime"].should eq(4.3)
|
|
all["idleMinWaitTime"].should eq(0.22)
|
|
all["idleMaxWaitTime"].should eq(3.5)
|
|
end
|
|
|
|
################################################################################
|
|
## state
|
|
################################################################################
|
|
|
|
it "checks the state" do
|
|
# fetch state
|
|
cmd = api + "/applier-state?global=true"
|
|
doc = ArangoDB.log_get("#{prefix}-applier-state", cmd, :body => "")
|
|
|
|
doc.code.should eq(200)
|
|
all = doc.parsed_response
|
|
all.should have_key('state')
|
|
all.should have_key('server')
|
|
|
|
state = all['state']
|
|
state['running'].should eq(false)
|
|
state.should have_key("lastAppliedContinuousTick")
|
|
state.should have_key("lastProcessedContinuousTick")
|
|
state.should have_key("lastAvailableContinuousTick")
|
|
state.should have_key("safeResumeTick")
|
|
|
|
state.should have_key("progress")
|
|
progress = state['progress']
|
|
progress.should have_key("time")
|
|
progress['time'].should match(/^(\d+-\d+-\d+T\d+:\d+:\d+Z)?$/)
|
|
progress.should have_key("failedConnects")
|
|
|
|
state.should have_key("totalRequests")
|
|
state.should have_key("totalFailedConnects")
|
|
state.should have_key("totalEvents")
|
|
state.should have_key("totalOperationsExcluded")
|
|
|
|
state.should have_key("lastError")
|
|
lastError = state["lastError"]
|
|
lastError.should have_key("errorNum")
|
|
state.should have_key("time")
|
|
state['time'].should match(/^\d+-\d+-\d+T\d+:\d+:\d+Z$/)
|
|
end
|
|
end
|
|
|
|
################################################################################
|
|
## wal access
|
|
################################################################################
|
|
|
|
context "dealing with wal access api" do
|
|
|
|
api = "/_api/wal"
|
|
prefix = "api-wal"
|
|
|
|
################################################################################
|
|
## state
|
|
################################################################################
|
|
|
|
it "check the state" do
|
|
# fetch state
|
|
cmd = "/_api/replication/logger-state"
|
|
doc = ArangoDB.log_get("api-replication-logger-state", cmd, :body => "")
|
|
|
|
doc.code.should eq(200)
|
|
|
|
all = doc.parsed_response
|
|
all.should have_key('state')
|
|
all.should have_key('server')
|
|
all.should have_key('clients')
|
|
|
|
state = all['state']
|
|
state['running'].should eq(true)
|
|
state['lastLogTick'].should match(/^\d+$/)
|
|
state['time'].should match(/^\d+-\d+-\d+T\d+:\d+:\d+Z$/)
|
|
|
|
server = all['server']
|
|
server['serverId'].should match(/^\d+$/)
|
|
server.should have_key('version')
|
|
end
|
|
|
|
|
|
################################################################################
|
|
## tickRanges
|
|
################################################################################
|
|
|
|
it "fetches the available tick range" do
|
|
# fetch state
|
|
cmd = api + "/range"
|
|
doc = ArangoDB.log_get("#{prefix}-range", cmd, :body => "")
|
|
|
|
doc.code.should eq(200)
|
|
|
|
result = doc.parsed_response
|
|
result.size.should be > 0
|
|
|
|
result.should have_key('tickMin')
|
|
result.should have_key('tickMax')
|
|
result['tickMin'].should match(/^\d+$/)
|
|
result['tickMax'].should match(/^\d+$/)
|
|
result.should have_key('server')
|
|
result['server'].should have_key('serverId')
|
|
result['server']['serverId'].should match(/^\d+$/)
|
|
end
|
|
|
|
################################################################################
|
|
## lastTick
|
|
################################################################################
|
|
|
|
it "fetches the available tick range" do
|
|
# fetch state
|
|
cmd = api + "/lastTick"
|
|
doc = ArangoDB.log_get("#{prefix}-lastTick", cmd, :body => "")
|
|
|
|
doc.code.should eq(200)
|
|
|
|
result = doc.parsed_response
|
|
result.size.should be > 0
|
|
|
|
result.should have_key('tick')
|
|
result['tick'].should match(/^\d+$/)
|
|
result.should have_key('server')
|
|
result['server'].should have_key('serverId')
|
|
result['server']['serverId'].should match(/^\d+$/)
|
|
end
|
|
|
|
################################################################################
|
|
## follow
|
|
################################################################################
|
|
|
|
it "fetches the empty follow log" do
|
|
while 1
|
|
cmd = api + "/lastTick"
|
|
doc = ArangoDB.log_get("#{prefix}-lastTick", cmd, :body => "")
|
|
doc.code.should eq(200)
|
|
fromTick = doc.parsed_response["tick"]
|
|
|
|
cmd = api + "/tail?global=true&from=" + fromTick
|
|
doc = ArangoDB.log_get("#{prefix}-tail", cmd, :body => "", :format => :plain)
|
|
|
|
if doc.code != 204
|
|
# someone else did something else
|
|
doc.code.should eq(200)
|
|
# sleep for a second and try again
|
|
sleep 1
|
|
else
|
|
doc.code.should eq(204)
|
|
|
|
# doc.headers["x-arango-replication-checkmore"].should eq("false")
|
|
doc.headers["x-arango-replication-lastincluded"].should match(/^\d+$/)
|
|
doc.headers["x-arango-replication-lastincluded"].should eq("0")
|
|
doc.headers["content-type"].should eq("application/x-arango-dump; charset=utf-8")
|
|
|
|
body = doc.response.body
|
|
body.should eq(nil)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
it "fetches a create collection action from the follow log" do
|
|
cid = 0
|
|
cuid = 0
|
|
doc = {}
|
|
|
|
while 1
|
|
ArangoDB.drop_collection("UnitTestsReplication")
|
|
|
|
sleep 5
|
|
|
|
cmd = api + "/lastTick"
|
|
doc = ArangoDB.log_get("#{prefix}-lastTick", cmd, :body => "")
|
|
doc.code.should eq(200)
|
|
fromTick = doc.parsed_response["tick"]
|
|
|
|
cid = ArangoDB.create_collection("UnitTestsReplication")
|
|
cuid = ArangoDB.properties_collection(cid)["globallyUniqueId"]
|
|
|
|
sleep 1
|
|
|
|
cmd = api + "/tail?global=true&from=" + fromTick
|
|
doc = ArangoDB.log_get("#{prefix}-follow-create-collection", cmd, :body => "", :format => :plain)
|
|
[200, 204].should include(doc.code)
|
|
|
|
break if doc.headers["x-arango-replication-frompresent"] == "true"
|
|
end
|
|
|
|
doc.headers["x-arango-replication-lastincluded"].should match(/^\d+$/)
|
|
doc.headers["x-arango-replication-lastincluded"].should_not eq("0")
|
|
doc.headers["content-type"].should eq("application/x-arango-dump; charset=utf-8")
|
|
|
|
body = doc.response.body
|
|
|
|
while 1
|
|
position = body.index("\n")
|
|
|
|
break if position == nil
|
|
|
|
part = body.slice(0, position)
|
|
document = JSON.parse(part)
|
|
|
|
if document["type"] == 2000 and document["cuid"] == cuid
|
|
document.should have_key("tick")
|
|
document.should have_key("type")
|
|
document.should have_key("cuid")
|
|
document.should have_key("data")
|
|
|
|
document["tick"].should match(/^\d+$/)
|
|
document["tick"].to_i.should >= fromTick.to_i
|
|
document["type"].should eq(2000)
|
|
document["cuid"].should eq(cuid)
|
|
|
|
c = document["data"]
|
|
c.should have_key("version")
|
|
c["type"].should eq(2)
|
|
c["cid"].should eq(cid)
|
|
c["cid"].should be_kind_of(String)
|
|
c["globallyUniqueId"].should eq(cuid)
|
|
c["deleted"].should eq(false)
|
|
c["name"].should eq("UnitTestsReplication")
|
|
c["waitForSync"].should eq(true)
|
|
end
|
|
|
|
body = body.slice(position + 1, body.length)
|
|
end
|
|
|
|
end
|
|
|
|
it "fetches some collection operations from the follow log" do
|
|
while 1
|
|
ArangoDB.drop_collection("UnitTestsReplication")
|
|
|
|
sleep 5
|
|
|
|
cmd = api + "/lastTick"
|
|
doc = ArangoDB.log_get("#{prefix}-follow-collection", cmd, :body => "")
|
|
doc.code.should eq(200)
|
|
fromTick = doc.parsed_response["tick"]
|
|
|
|
# create collection
|
|
cid = ArangoDB.create_collection("UnitTestsReplication")
|
|
cuid = ArangoDB.properties_collection(cid)["globallyUniqueId"]
|
|
|
|
# create document
|
|
cmd = "/_api/document?collection=UnitTestsReplication"
|
|
body = "{ \"_key\" : \"test\", \"test\" : false }"
|
|
doc = ArangoDB.log_post("#{prefix}-follow-collection", cmd, :body => body)
|
|
doc.code.should eq(201)
|
|
rev = doc.parsed_response["_rev"]
|
|
|
|
# update document
|
|
cmd = "/_api/document/UnitTestsReplication/test"
|
|
body = "{ \"updated\" : true }"
|
|
doc = ArangoDB.log_patch("#{prefix}-follow-collection", cmd, :body => body)
|
|
doc.code.should eq(201)
|
|
rev2 = doc.parsed_response["_rev"]
|
|
|
|
# delete document
|
|
cmd = "/_api/document/UnitTestsReplication/test"
|
|
doc = ArangoDB.log_delete("#{prefix}-follow-collection", cmd)
|
|
doc.code.should eq(200)
|
|
|
|
sleep 1
|
|
|
|
cmd = api + "/tail?global=true&from=" + fromTick
|
|
doc = ArangoDB.log_get("#{prefix}-follow-create-collection", cmd, :body => "", :format => :plain)
|
|
[200, 204].should include(doc.code)
|
|
body = doc.response.body
|
|
|
|
break if doc.headers["x-arango-replication-frompresent"] == "true"
|
|
end
|
|
|
|
doc.headers["x-arango-replication-lastincluded"].should match(/^\d+$/)
|
|
doc.headers["x-arango-replication-lastincluded"].should_not eq("0")
|
|
doc.headers["content-type"].should eq("application/x-arango-dump; charset=utf-8")
|
|
|
|
i = 0
|
|
while 1
|
|
position = body.index("\n")
|
|
|
|
break if position == nil
|
|
|
|
part = body.slice(0, position)
|
|
document = JSON.parse(part)
|
|
|
|
if i == 0
|
|
if document["type"] == 2000 and document["cuid"] == cuid
|
|
# create collection
|
|
document.should have_key("tick")
|
|
document.should have_key("type")
|
|
document.should have_key("cuid")
|
|
document.should have_key("data")
|
|
|
|
document["tick"].should match(/^\d+$/)
|
|
document["tick"].to_i.should >= fromTick.to_i
|
|
document["type"].should eq(2000)
|
|
document["cuid"].should eq(cuid)
|
|
|
|
c = document["data"]
|
|
c.should have_key("version")
|
|
c["type"].should eq(2)
|
|
c["cid"].should eq(cid)
|
|
c["cid"].should be_kind_of(String)
|
|
c["globallyUniqueId"].should eq(cuid)
|
|
c["deleted"].should eq(false)
|
|
c["name"].should eq("UnitTestsReplication")
|
|
c["waitForSync"].should eq(true)
|
|
|
|
i = i + 1
|
|
end
|
|
elsif i == 1 and document["type"] == 2300 and document["cuid"] == cuid
|
|
# create document
|
|
document.should have_key("tick")
|
|
document.should have_key("type")
|
|
document.should have_key("cuid")
|
|
|
|
document["tick"].should match(/^\d+$/)
|
|
document["tick"].to_i.should >= fromTick.to_i
|
|
document["type"].should eq(2300)
|
|
document["cuid"].should eq(cuid)
|
|
document["data"]["_key"].should eq("test")
|
|
document["data"]["_rev"].should match(/^[a-zA-Z0-9_\-]+$/)
|
|
document["data"]["_rev"].should eq(rev)
|
|
document["data"]["test"].should eq(false)
|
|
|
|
i = i + 1
|
|
elsif i == 2 and document["cuid"] == cuid
|
|
# update document, there must only be 2300 no 2302
|
|
document.should have_key("tick")
|
|
document.should have_key("type")
|
|
document.should have_key("cuid")
|
|
|
|
document["tick"].should match(/^\d+$/)
|
|
document["tick"].to_i.should >= fromTick.to_i
|
|
document["type"].should eq(2300)
|
|
document["cuid"].should eq(cuid)
|
|
document["data"]["_key"].should eq("test")
|
|
document["data"]["_rev"].should match(/^[a-zA-Z0-9_\-]+$/)
|
|
document["data"]["_rev"].should eq(rev2)
|
|
document["data"]["test"].should eq(false)
|
|
|
|
i = i + 1
|
|
elsif i == 3 and document["type"] == 2302 and document["cuid"] == cuid
|
|
# delete document
|
|
document.should have_key("tick")
|
|
document.should have_key("type")
|
|
document.should have_key("cuid")
|
|
|
|
document["tick"].should match(/^\d+$/)
|
|
document["tick"].to_i.should >= fromTick.to_i
|
|
document["type"].should eq(2302)
|
|
document["cuid"].should eq(cuid)
|
|
document["data"]["_key"].should eq("test")
|
|
document["data"]["_rev"].should match(/^[a-zA-Z0-9_\-]+$/)
|
|
document["data"]["_rev"].should_not eq(rev)
|
|
|
|
i = i + 1
|
|
end
|
|
|
|
body = body.slice(position + 1, body.length)
|
|
end
|
|
|
|
i.should eq(4)
|
|
|
|
# tail you later
|
|
cmd = api + "/lastTick"
|
|
doc = ArangoDB.log_get("#{prefix}-follow-collection", cmd, :body => "")
|
|
doc.code.should eq(200)
|
|
fromTick = doc.parsed_response["tick"]
|
|
|
|
# drop collection
|
|
cmd = "/_api/collection/UnitTestsReplication"
|
|
doc = ArangoDB.log_delete("#{prefix}-follow-collection", cmd)
|
|
doc.code.should eq(200)
|
|
|
|
while 1
|
|
sleep 1
|
|
|
|
cmd = api + "/tail?global=true&from=" + fromTick
|
|
doc = ArangoDB.log_get("#{prefix}-follow-create-collection", cmd, :body => "", :format => :plain)
|
|
[200, 204].should include(doc.code)
|
|
body = doc.response.body
|
|
|
|
break if doc.headers["x-arango-replication-frompresent"] == "true"
|
|
end
|
|
|
|
while 1
|
|
position = body.index("\n")
|
|
|
|
break if position == nil
|
|
|
|
part = body.slice(0, position)
|
|
document = JSON.parse(part)
|
|
|
|
if document["type"] == 2001 and document["cuid"] == cuid
|
|
# drop collection
|
|
document.should have_key("tick")
|
|
document.should have_key("type")
|
|
document.should have_key("cuid")
|
|
|
|
document["tick"].should match(/^\d+$/)
|
|
document["tick"].to_i.should >= fromTick.to_i
|
|
document["type"].should eq(2001)
|
|
document["cuid"].should eq(cuid)
|
|
|
|
i = i + 1
|
|
end
|
|
|
|
body = body.slice(position + 1, body.length)
|
|
end
|
|
|
|
i.should eq(5)
|
|
|
|
end
|
|
|
|
it "fetches some more single document operations from the log" do
|
|
ArangoDB.drop_collection("UnitTestsReplication")
|
|
|
|
sleep 5
|
|
|
|
cmd = api + "/lastTick"
|
|
doc = ArangoDB.log_get("#{prefix}-follow-collection", cmd, :body => "")
|
|
doc.code.should eq(200)
|
|
fromTick = doc.parsed_response["tick"]
|
|
|
|
# create collection
|
|
cid = ArangoDB.create_collection("UnitTestsReplication")
|
|
cuid = ArangoDB.properties_collection(cid)["globallyUniqueId"]
|
|
|
|
# create document
|
|
cmd = "/_api/document?collection=UnitTestsReplication"
|
|
body = "{ \"_key\" : \"test\", \"test\" : false }"
|
|
doc = ArangoDB.log_post("#{prefix}-follow-collection", cmd, :body => body)
|
|
doc.code.should eq(201)
|
|
rev = doc.parsed_response["_rev"]
|
|
|
|
# update document
|
|
cmd = "/_api/document/UnitTestsReplication/test"
|
|
body = "{ \"updated\" : true }"
|
|
doc = ArangoDB.log_patch("#{prefix}-follow-collection", cmd, :body => body)
|
|
doc.code.should eq(201)
|
|
rev2 = doc.parsed_response["_rev"]
|
|
|
|
# delete document
|
|
cmd = "/_api/document/UnitTestsReplication/test"
|
|
doc = ArangoDB.log_delete("#{prefix}-follow-collection", cmd)
|
|
doc.code.should eq(200)
|
|
|
|
sleep 5
|
|
|
|
cmd = api + "/tail?global=true&from=" + fromTick
|
|
doc = ArangoDB.log_get("#{prefix}-follow-create-collection", cmd, :body => "", :format => :plain)
|
|
doc.code.should eq(200)
|
|
|
|
doc.headers["x-arango-replication-lastincluded"].should match(/^\d+$/)
|
|
doc.headers["x-arango-replication-lastincluded"].should_not eq("0")
|
|
doc.headers["content-type"].should eq("application/x-arango-dump; charset=utf-8")
|
|
|
|
tickTypes = { 2000 => 0, 2001 => 0, 2300 => 0, 2302 => 0 }
|
|
|
|
body = doc.response.body
|
|
|
|
cmd = api + "/lastTick"
|
|
doc = ArangoDB.log_get("#{prefix}-follow-collection", cmd, :body => "")
|
|
doc.code.should eq(200)
|
|
|
|
i = 0
|
|
while 1
|
|
position = body.index("\n")
|
|
|
|
break if position == nil
|
|
|
|
part = body.slice(0, position)
|
|
marker = JSON.parse(part)
|
|
|
|
if marker["type"] >= 2000 and marker["cuid"] == cuid
|
|
# create collection
|
|
marker.should have_key("tick")
|
|
marker.should have_key("type")
|
|
marker.should have_key("cuid")
|
|
|
|
if marker["type"] == 2300
|
|
marker.should have_key("data")
|
|
end
|
|
|
|
cc = tickTypes[marker["type"]]
|
|
tickTypes[marker["type"]] = cc + 1
|
|
end
|
|
body = body.slice(position + 1, body.length)
|
|
end
|
|
|
|
tickTypes[2000].should eq(1) # collection create
|
|
tickTypes[2001].should eq(0) # collection drop
|
|
tickTypes[2300].should eq(2) # document insert / update
|
|
tickTypes[2302].should eq(1) # document drop
|
|
|
|
# drop collection
|
|
cmd = "/_api/collection/UnitTestsReplication"
|
|
doc = ArangoDB.log_delete("#{prefix}-follow-collection", cmd)
|
|
doc.code.should eq(200)
|
|
end
|
|
|
|
it "validates chunkSize restrictions" do
|
|
ArangoDB.drop_collection("UnitTestsReplication")
|
|
|
|
sleep 1
|
|
|
|
cmd = api + "/lastTick"
|
|
doc = ArangoDB.log_get("#{prefix}-follow-chunksize", cmd, :body => "")
|
|
doc.code.should eq(200)
|
|
fromTick = doc.parsed_response["tick"]
|
|
originalTick = fromTick
|
|
lastScanned = fromTick
|
|
|
|
# create collection
|
|
cid = ArangoDB.create_collection("UnitTestsReplication")
|
|
cuid = ArangoDB.properties_collection(cid)["globallyUniqueId"]
|
|
|
|
# create documents
|
|
(1..250).each do |value|
|
|
cmd = "/_api/document?collection=UnitTestsReplication"
|
|
body = "{ \"value\" : \"thisIsALongerStringBecauseWeWantToTestTheChunkSizeLimitsLaterOnAndItGetsEvenLongerWithTimeForRealNow\" }"
|
|
doc = ArangoDB.log_post("#{prefix}-follow-chunksize", cmd, :body => body)
|
|
doc.code.should eq(201)
|
|
end
|
|
|
|
# create one big transaction
|
|
docsBody = "["
|
|
(1..749).each do |value|
|
|
docsBody << "{ \"value\" : \"%d\" }," % [value]
|
|
end
|
|
docsBody << "{ \"value\" : \"500\" }]"
|
|
cmd = "/_api/document?collection=UnitTestsReplication"
|
|
doc = ArangoDB.log_post("#{prefix}-follow-chunksize", cmd, :body => docsBody)
|
|
doc.code.should eq(201)
|
|
|
|
# create more documents
|
|
(1..500).each do |value|
|
|
cmd = "/_api/document?collection=UnitTestsReplication"
|
|
body = "{ \"value\" : \"thisIsALongerStringBecauseWeWantToTestTheChunkSizeLimitsLaterOnAndItGetsEvenLongerWithTimeForRealNow\" }"
|
|
doc = ArangoDB.log_post("#{prefix}-follow-chunksize", cmd, :body => body)
|
|
doc.code.should eq(201)
|
|
end
|
|
|
|
sleep 1
|
|
|
|
tickTypes = { 2000 => 0, 2001 => 0, 2200 => 0, 2201 => 0, 2300 => 0 }
|
|
|
|
while 1
|
|
cmd = api + "/tail?global=true&from=" + fromTick + "&lastScanned=" + lastScanned + "&chunkSize=16384"
|
|
doc = ArangoDB.log_get("#{prefix}-follow-chunksize", cmd, :body => "", :format => :plain)
|
|
[200, 204].should include(doc.code)
|
|
|
|
break if doc.code == 204
|
|
|
|
doc.headers["x-arango-replication-lastincluded"].should match(/^\d+$/)
|
|
doc.headers["x-arango-replication-lastincluded"].should_not eq("0")
|
|
doc.headers["x-arango-replication-lastscanned"].should match(/^\d+$/)
|
|
doc.headers["x-arango-replication-lastscanned"].should_not eq("0")
|
|
if fromTick == originalTick
|
|
# first batch
|
|
doc.headers["x-arango-replication-checkmore"].should eq("true")
|
|
end
|
|
doc.headers["content-type"].should eq("application/x-arango-dump; charset=utf-8")
|
|
# we need to allow for some overhead here, as the chunkSize restriction is not honored precisely
|
|
doc.headers["content-length"].to_i.should be < (16 + 9) * 1024
|
|
|
|
# update lastScanned for next request
|
|
lastScanned = doc.headers["x-arango-replication-lastscanned"]
|
|
body = doc.response.body
|
|
|
|
i = 0
|
|
while 1
|
|
position = body.index("\n")
|
|
|
|
break if position == nil
|
|
|
|
part = body.slice(0, position)
|
|
marker = JSON.parse(part)
|
|
|
|
# update last tick value
|
|
marker.should have_key("tick")
|
|
fromTick = marker["tick"]
|
|
|
|
if marker["type"] == 2200
|
|
marker.should have_key("tid")
|
|
marker.should have_key("db")
|
|
tickTypes[2200] = tickTypes[2200] + 1
|
|
|
|
elsif marker["type"] == 2201
|
|
marker.should have_key("tid")
|
|
tickTypes[2201] = tickTypes[2201] + 1
|
|
|
|
elsif marker["type"] >= 2000 and marker["cuid"] == cuid
|
|
# collection markings
|
|
marker.should have_key("type")
|
|
marker.should have_key("cuid")
|
|
|
|
if marker["type"] == 2300
|
|
marker.should have_key("data")
|
|
marker.should have_key("tid")
|
|
end
|
|
|
|
cc = tickTypes[marker["type"]]
|
|
tickTypes[marker["type"]] = cc + 1
|
|
|
|
break if tickTypes[2300] == 1500
|
|
end
|
|
body = body.slice(position + 1, body.length)
|
|
end
|
|
end
|
|
|
|
tickTypes[2000].should eq(1) # collection create
|
|
tickTypes[2001].should eq(0) # collection drop
|
|
tickTypes[2200].should be >= 1 # begin transaction
|
|
tickTypes[2201].should be >= 1 # commit transaction
|
|
tickTypes[2300].should eq(1500) # document inserts
|
|
|
|
|
|
# now try again with a single chunk
|
|
tickTypes = { 2000 => 0, 2001 => 0, 2200 => 0, 2201 => 0, 2300 => 0 }
|
|
|
|
cmd = api + "/tail?global=true&from=" + originalTick + "&lastScanned=" + originalTick + "&chunkSize=1048576"
|
|
doc = ArangoDB.log_get("#{prefix}-follow-chunksize", cmd, :body => "", :format => :plain)
|
|
doc.code.should eq(200)
|
|
|
|
doc.headers["x-arango-replication-lastincluded"].should match(/^\d+$/)
|
|
doc.headers["x-arango-replication-lastincluded"].should_not eq("0")
|
|
doc.headers["content-type"].should eq("application/x-arango-dump; charset=utf-8")
|
|
|
|
# we need to allow for some overhead here, as the chunkSize restriction is not honored precisely
|
|
doc.headers["content-length"].to_i.should be > (16 + 8) * 1024
|
|
|
|
body = doc.response.body
|
|
|
|
i = 0
|
|
while 1
|
|
position = body.index("\n")
|
|
|
|
break if position == nil
|
|
|
|
part = body.slice(0, position)
|
|
marker = JSON.parse(part)
|
|
|
|
marker.should have_key("tick")
|
|
|
|
if marker["type"] == 2200
|
|
marker.should have_key("tid")
|
|
marker.should have_key("db")
|
|
tickTypes[2200] = tickTypes[2200] + 1
|
|
|
|
elsif marker["type"] == 2201
|
|
marker.should have_key("tid")
|
|
tickTypes[2201] = tickTypes[2201] + 1
|
|
|
|
elsif marker["type"] >= 2000 and marker["cuid"] == cuid
|
|
# create collection
|
|
marker.should have_key("type")
|
|
marker.should have_key("cuid")
|
|
|
|
if marker["type"] == 2300
|
|
marker.should have_key("data")
|
|
marker.should have_key("tid")
|
|
end
|
|
|
|
cc = tickTypes[marker["type"]]
|
|
tickTypes[marker["type"]] = cc + 1
|
|
|
|
break if tickTypes[2300] == 1500
|
|
end
|
|
body = body.slice(position + 1, body.length)
|
|
end
|
|
|
|
tickTypes[2000].should eq(1) # collection create
|
|
tickTypes[2001].should eq(0) # collection drop
|
|
tickTypes[2200].should be >= 1 # begin transaction
|
|
tickTypes[2201].should be >= 1 # commit transaction
|
|
tickTypes[2300].should eq(1500) # document inserts
|
|
|
|
# drop collection
|
|
cmd = "/_api/collection/UnitTestsReplication"
|
|
doc = ArangoDB.log_delete("#{prefix}-follow-chunksize", cmd)
|
|
doc.code.should eq(200)
|
|
end
|
|
end
|
|
|
|
################################################################################
|
|
## inventory / dump
|
|
################################################################################
|
|
|
|
context "dealing with the initial dump" do
|
|
|
|
api = "/_api/replication"
|
|
prefix = "api-replication"
|
|
|
|
before do
|
|
ArangoDB.drop_collection("UnitTestsReplication")
|
|
ArangoDB.drop_collection("UnitTestsReplication2")
|
|
doc = ArangoDB.post(api + "/batch", :body => "{}")
|
|
doc.code.should eq(200)
|
|
@batchId = doc.parsed_response['id']
|
|
@batchId.should match(/^\d+$/)
|
|
end
|
|
|
|
after do
|
|
ArangoDB.delete(api + "/batch/#{@batchId}", :body => "")
|
|
ArangoDB.drop_collection("UnitTestsReplication")
|
|
ArangoDB.drop_collection("UnitTestsReplication2")
|
|
end
|
|
|
|
################################################################################
|
|
## inventory
|
|
################################################################################
|
|
|
|
it "checks the initial inventory" do
|
|
cmd = api + "/inventory?includeSystem=true&global=true&batchId=#{@batchId}"
|
|
doc = ArangoDB.log_get("#{prefix}-inventory", cmd, :body => "")
|
|
|
|
doc.code.should eq(200)
|
|
all = doc.parsed_response
|
|
all.should have_key('databases')
|
|
all.should have_key('state')
|
|
state = all['state']
|
|
state['running'].should eq(true)
|
|
state['lastLogTick'].should match(/^\d+$/)
|
|
state['time'].should match(/^\d+-\d+-\d+T\d+:\d+:\d+Z$/)
|
|
|
|
databases = all['databases']
|
|
databases.each { |name, database|
|
|
database.should have_key('collections')
|
|
|
|
collections = database["collections"]
|
|
filtered = [ ]
|
|
collections.each { |collection|
|
|
if [ "UnitTestsReplication", "UnitTestsReplication2" ].include? collection["parameters"]["name"]
|
|
filtered.push collection
|
|
end
|
|
collection["parameters"].should have_key('globallyUniqueId')
|
|
}
|
|
filtered.should eq([ ])
|
|
}
|
|
end
|
|
|
|
it "checks the inventory after creating collections" do
|
|
cid = ArangoDB.create_collection("UnitTestsReplication", false)
|
|
cuid = ArangoDB.properties_collection(cid)["globallyUniqueId"]
|
|
cid2 = ArangoDB.create_collection("UnitTestsReplication2", true, 3)
|
|
cuid2 = ArangoDB.properties_collection(cid2)["globallyUniqueId"]
|
|
|
|
cmd = api + "/inventory?includeSystem=true&global=true&batchId=#{@batchId}"
|
|
doc = ArangoDB.log_get("#{prefix}-inventory-create", cmd, :body => "")
|
|
doc.code.should eq(200)
|
|
all = doc.parsed_response
|
|
|
|
all.should have_key('state')
|
|
state = all['state']
|
|
state['running'].should eq(true)
|
|
state['lastLogTick'].should match(/^\d+$/)
|
|
state['time'].should match(/^\d+-\d+-\d+T\d+:\d+:\d+Z$/)
|
|
|
|
all.should have_key('databases')
|
|
databases = all['databases']
|
|
|
|
filtered = [ ]
|
|
databases.each { |name, database|
|
|
database.should have_key('collections')
|
|
|
|
collections = database["collections"]
|
|
filtered = [ ]
|
|
collections.each { |collection|
|
|
if [ "UnitTestsReplication", "UnitTestsReplication2" ].include? collection["parameters"]["name"]
|
|
filtered.push collection
|
|
end
|
|
collection["parameters"].should have_key('globallyUniqueId')
|
|
}
|
|
}
|
|
filtered.length.should eq(2)
|
|
|
|
# first collection
|
|
c = filtered[0]
|
|
c.should have_key("parameters")
|
|
c.should have_key("indexes")
|
|
|
|
parameters = c['parameters']
|
|
parameters.should have_key("version")
|
|
parameters["version"].should be_kind_of(Integer)
|
|
parameters["type"].should be_kind_of(Integer)
|
|
parameters["type"].should eq(2)
|
|
parameters["cid"].should eq(cid)
|
|
parameters["cid"].should be_kind_of(String)
|
|
parameters["deleted"].should eq(false)
|
|
parameters["name"].should eq("UnitTestsReplication")
|
|
parameters["waitForSync"].should eq(false)
|
|
parameters.should have_key("globallyUniqueId")
|
|
parameters["globallyUniqueId"].should eq(cuid)
|
|
|
|
c['indexes'].should eq([ ])
|
|
|
|
# second collection
|
|
c = filtered[1]
|
|
c.should have_key("parameters")
|
|
c.should have_key("indexes")
|
|
|
|
parameters = c['parameters']
|
|
parameters.should have_key("version")
|
|
parameters["version"].should be_kind_of(Integer)
|
|
parameters["type"].should be_kind_of(Integer)
|
|
parameters["type"].should eq(3)
|
|
parameters["cid"].should eq(cid2)
|
|
parameters["cid"].should be_kind_of(String)
|
|
parameters["deleted"].should eq(false)
|
|
parameters["name"].should eq("UnitTestsReplication2")
|
|
parameters["waitForSync"].should eq(true)
|
|
parameters.should have_key("globallyUniqueId")
|
|
parameters["globallyUniqueId"].should eq(cuid2)
|
|
|
|
c['indexes'].should eq([ ])
|
|
end
|
|
|
|
it "checks the inventory with indexes" do
|
|
cid = ArangoDB.create_collection("UnitTestsReplication", false)
|
|
cuid = ArangoDB.properties_collection(cid)["globallyUniqueId"]
|
|
cid2 = ArangoDB.create_collection("UnitTestsReplication2", false)
|
|
cuid2 = ArangoDB.properties_collection(cid2)["globallyUniqueId"]
|
|
|
|
body = "{ \"type\" : \"hash\", \"unique\" : false, \"fields\" : [ \"a\", \"b\" ] }"
|
|
doc = ArangoDB.log_post("#{prefix}-inventory2", "/_api/index?collection=UnitTestsReplication", :body => body)
|
|
doc.code.should eq(201)
|
|
|
|
body = "{ \"type\" : \"skiplist\", \"unique\" : false, \"fields\" : [ \"c\" ] }"
|
|
doc = ArangoDB.log_post("#{prefix}-inventory2", "/_api/index?collection=UnitTestsReplication", :body => body)
|
|
doc.code.should eq(201)
|
|
|
|
# create indexes for second collection
|
|
body = "{ \"type\" : \"skiplist\", \"unique\" : true, \"fields\" : [ \"d\" ] }"
|
|
doc = ArangoDB.log_post("#{prefix}-inventory2", "/_api/index?collection=UnitTestsReplication2", :body => body)
|
|
doc.code.should eq(201)
|
|
|
|
cmd = api + "/inventory?batchId=#{@batchId}&global=true"
|
|
doc = ArangoDB.log_get("#{prefix}-inventory2", cmd, :body => "")
|
|
doc.code.should eq(200)
|
|
|
|
all = doc.parsed_response
|
|
|
|
all.should have_key('state')
|
|
state = all['state']
|
|
state['running'].should eq(true)
|
|
state['lastLogTick'].should match(/^\d+$/)
|
|
state['time'].should match(/^\d+-\d+-\d+T\d+:\d+:\d+Z$/)
|
|
|
|
filtered = [ ]
|
|
all.should have_key('databases')
|
|
databases = all['databases']
|
|
databases.each { |name, database|
|
|
database.should have_key('collections')
|
|
collections = database['collections']
|
|
collections.each { |collection|
|
|
if [ "UnitTestsReplication", "UnitTestsReplication2" ].include? collection["parameters"]["name"]
|
|
filtered.push collection
|
|
end
|
|
}
|
|
}
|
|
|
|
filtered.length.should eq(2)
|
|
|
|
# first collection
|
|
c = filtered[0]
|
|
c.should have_key("parameters")
|
|
c.should have_key("indexes")
|
|
|
|
parameters = c['parameters']
|
|
parameters.should have_key("version")
|
|
parameters["version"].should be_kind_of(Integer)
|
|
parameters["type"].should be_kind_of(Integer)
|
|
parameters["type"].should eq(2)
|
|
parameters["cid"].should eq(cid)
|
|
parameters["cid"].should be_kind_of(String)
|
|
parameters["deleted"].should eq(false)
|
|
parameters["name"].should eq("UnitTestsReplication")
|
|
parameters["waitForSync"].should eq(false)
|
|
parameters.should have_key("globallyUniqueId")
|
|
parameters["globallyUniqueId"].should eq(cuid)
|
|
|
|
indexes = c['indexes']
|
|
indexes.length.should eq(2)
|
|
|
|
idx = indexes[0]
|
|
idx["id"].should match(/^\d+$/)
|
|
idx["type"].should eq("hash")
|
|
idx["unique"].should eq(false)
|
|
idx["fields"].should eq([ "a", "b" ])
|
|
|
|
idx = indexes[1]
|
|
idx["id"].should match(/^\d+$/)
|
|
idx["type"].should eq("skiplist")
|
|
idx["unique"].should eq(false)
|
|
idx["fields"].should eq([ "c" ])
|
|
|
|
# second collection
|
|
c = filtered[1]
|
|
c.should have_key("parameters")
|
|
c.should have_key("indexes")
|
|
|
|
parameters = c['parameters']
|
|
parameters.should have_key("version")
|
|
parameters["version"].should be_kind_of(Integer)
|
|
parameters["type"].should be_kind_of(Integer)
|
|
parameters["type"].should eq(2)
|
|
parameters["cid"].should eq(cid2)
|
|
parameters["cid"].should be_kind_of(String)
|
|
parameters["deleted"].should eq(false)
|
|
parameters["name"].should eq("UnitTestsReplication2")
|
|
parameters["waitForSync"].should eq(false)
|
|
parameters.should have_key("globallyUniqueId")
|
|
parameters["globallyUniqueId"].should eq(cuid2)
|
|
|
|
indexes = c['indexes']
|
|
indexes.length.should eq(1)
|
|
|
|
idx = indexes[0]
|
|
idx["id"].should match(/^\d+$/)
|
|
idx["type"].should eq("skiplist")
|
|
idx["unique"].should eq(true)
|
|
idx["fields"].should eq([ "d" ])
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
## TODO: add CRUD tests for updates
|
|
|
|
end
|