1- import Instance from "arangodb-instance-manager/lib/Instance" ;
21import InstanceManager from "arangodb-instance-manager/lib/InstanceManager" ;
32import { expect } from "chai" ;
43import { resolve } from "path" ;
5- import { DocumentCollection } from "../collection" ;
64import { Connection } from "../connection" ;
75import { Database } from "../database" ;
86
9- async function sleep ( ms : number ) {
10- return new Promise < void > ( ( resolve ) => {
11- setTimeout ( ( ) => resolve ( ) , ms ) ;
12- } ) ;
13- }
14-
157let ARANGO_PATH = "" ;
168let ARANGO_RUNNER : "local" | "docker" ;
179if ( process . env . RESILIENCE_ARANGO_BASEPATH ) {
@@ -28,161 +20,6 @@ if (process.env.RESILIENCE_ARANGO_BASEPATH) {
2820}
2921const describeIm = ARANGO_PATH ? describe . only : describe . skip ;
3022
31- describeIm ( "Single-server active failover" , function ( ) {
32- this . timeout ( Infinity ) ;
33- let im : InstanceManager ;
34- let uuid : string ;
35- let leader : Instance ;
36- let db : Database ;
37- let conn : Connection ;
38- beforeEach ( async ( ) => {
39- im = new InstanceManager ( ARANGO_PATH , ARANGO_RUNNER , "rocksdb" ) ;
40- await im . startAgency ( ) ;
41- await im . startSingleServer ( "arangojs" , 2 ) ;
42- await im . waitForAllInstances ( ) ;
43- uuid = await im . asyncReplicationLeaderSelected ( ) ;
44- leader = ( await im . resolveUUID ( uuid ) ) ! ;
45- db = new Database ( { url : leader . endpoint } ) ;
46- conn = ( db as any ) . _connection ;
47- await db . acquireHostList ( ) ;
48- } ) ;
49- afterEach ( async function ( ) {
50- im . moveServerLogs ( this . currentTest ) ;
51- const logs = await im . cleanup ( this . currentTest ! . isFailed ( ) ) ;
52- // eslint-disable-next-line no-console
53- if ( logs ) console . error ( `IM Logs:\n${ logs } ` ) ;
54- } ) ;
55- async function getServerId ( ) : Promise < string | undefined > {
56- const res = await db . route ( "_api/replication/server-id" ) . get ( ) ;
57- return res . body . serverId ;
58- }
59- async function responseHeaders ( ) {
60- const res = await db . route ( "_api/version" ) . get ( ) ;
61- return res . headers ;
62- }
63- it . skip ( "failover to follower if leader is down" , async function ( ) {
64- // This test times out on GitHub Actions during leader selection.
65- // This is likely an issue with the Instance Manager, not arangojs.
66- expect ( ( conn as any ) . _hostUrls ) . to . have . lengthOf ( 2 ) ;
67- ( conn as any ) . _activeHostUrl = ( conn as any ) . _hostUrls [ 0 ] ;
68- const leaderId = await getServerId ( ) ;
69- expect ( leaderId ) . not . to . be . empty ;
70- const headers = await responseHeaders ( ) ;
71- expect ( headers ) . not . to . include . keys ( "x-arango-endpoint" ) ;
72-
73- await im . kill ( leader ) ;
74- await im . asyncReplicationLeaderSelected ( uuid as any ) ;
75- await sleep ( 3000 ) ;
76- await db . version ( ) ; // cycle
77-
78- const newLeaderId = await getServerId ( ) ;
79- expect ( newLeaderId ) . not . to . be . empty ;
80- expect ( newLeaderId ) . not . to . equal ( leaderId ) ;
81- } ) ;
82- it ( "redirect to leader if server is not leader" , async ( ) => {
83- expect ( ( conn as any ) . _hostUrls ) . to . have . lengthOf ( 2 ) ;
84-
85- ( conn as any ) . _activeHostUrl = ( conn as any ) . _hostUrls [ 0 ] ;
86- const leaderId = await getServerId ( ) ;
87- expect ( leaderId ) . not . to . be . empty ;
88- const leaderHeaders = await responseHeaders ( ) ;
89- expect ( leaderHeaders ) . not . to . include . keys ( "x-arango-endpoint" ) ;
90-
91- ( conn as any ) . _activeHostUrl = ( conn as any ) . _hostUrls [ 1 ] ;
92- const followerId = await getServerId ( ) ;
93- expect ( followerId ) . not . to . be . empty ;
94- expect ( followerId ) . not . to . equal ( leaderId ) ;
95- const followerHeaders = await responseHeaders ( ) ;
96- expect ( followerHeaders ) . to . include . keys ( "x-arango-endpoint" ) ;
97- ( conn as any ) . _hosts . shift ( ) ;
98- ( conn as any ) . _hostUrls . shift ( ) ;
99- ( conn as any ) . _activeHostUrl = ( conn as any ) . _hostUrls [ 0 ] ;
100- expect ( ( conn as any ) . _hostUrls ) . to . have . lengthOf ( 1 ) ;
101-
102- await db . createCollection ( `test_${ Date . now ( ) } ` ) ;
103- const newLeaderId = await getServerId ( ) ;
104- expect ( newLeaderId ) . not . to . be . empty ;
105- expect ( newLeaderId ) . to . equal ( leaderId ) ;
106- expect ( ( conn as any ) . _hostUrls ) . to . have . lengthOf ( 2 ) ;
107- } ) ;
108- } ) ;
109-
110- describeIm ( "Single-server with follower" , function ( ) {
111- this . timeout ( Infinity ) ;
112- let im : InstanceManager ;
113- let leader : Instance ;
114- let db : Database ;
115- let conn : Connection ;
116- let collection : DocumentCollection ;
117- beforeEach ( async ( ) => {
118- im = new InstanceManager ( ARANGO_PATH , ARANGO_RUNNER ) ;
119- await im . startAgency ( ) ;
120- await im . startSingleServer ( "arangojs" , 2 ) ;
121- await im . waitForAllInstances ( ) ;
122- leader = await im . asyncReplicationLeaderInstance ( ) ;
123- db = new Database ( { url : leader . endpoint } ) ;
124- conn = ( db as any ) . _connection ;
125- await db . acquireHostList ( ) ;
126- collection = await db . createCollection ( "test" ) ;
127- await db . waitForPropagation (
128- { path : `/_api/collection/${ collection . name } ` } ,
129- 10000
130- ) ;
131- await collection . save ( { _key : "abc" } ) ;
132- await sleep ( 3000 ) ;
133- } ) ;
134- afterEach ( async ( ) => {
135- await collection . drop ( ) ;
136- await sleep ( 3000 ) ;
137- await im . cleanup ( ) ;
138- } ) ;
139- async function getResponse ( dirty ?: boolean ) {
140- return await conn . request ( {
141- method : "GET" ,
142- path : "/_api/document/test/abc" ,
143- allowDirtyRead : dirty ,
144- } ) ;
145- }
146- it ( "supports dirty reads" , async ( ) => {
147- expect ( ( conn as any ) . _hostUrls ) . to . have . lengthOf ( 2 ) ;
148- const res1 = await getResponse ( true ) ;
149- expect ( res1 . arangojsHostUrl ) . to . be . a ( "string" ) ;
150- const headers1 = res1 . request . getHeaders ( ) ;
151- expect ( headers1 ) . to . include . keys ( "x-arango-allow-dirty-read" ) ;
152- const res2 = await getResponse ( true ) ;
153- expect ( res2 . arangojsHostUrl ) . to . be . a ( "string" ) ;
154- expect ( res2 . arangojsHostUrl ) . not . to . equal ( res1 . arangojsHostUrl ) ;
155- const headers2 = res2 . request . getHeaders ( ) ;
156- expect ( headers2 ) . to . include . keys ( "x-arango-allow-dirty-read" ) ;
157- } ) ;
158- it ( "supports non-dirty reads" , async ( ) => {
159- expect ( ( conn as any ) . _hostUrls ) . to . have . lengthOf ( 2 ) ;
160- const res1 = await getResponse ( ) ;
161- expect ( res1 . arangojsHostUrl ) . to . be . a ( "string" ) ;
162- const headers1 = res1 . request . getHeaders ( ) ;
163- expect ( headers1 ) . not . to . include . keys ( "x-arango-allow-dirty-read" ) ;
164- const res2 = await getResponse ( ) ;
165- expect ( res2 . arangojsHostUrl ) . to . be . a ( "string" ) ;
166- expect ( res2 . arangojsHostUrl ) . to . equal ( res1 . arangojsHostUrl ) ;
167- const headers2 = res2 . request . getHeaders ( ) ;
168- expect ( headers2 ) . not . to . include . keys ( "x-arango-allow-dirty-read" ) ;
169- } ) ;
170- it ( "supports dirty read over multiple cursor batches" , async ( ) => {
171- const cursor = await db . query (
172- "FOR i IN 1..2 RETURN i" ,
173- { } ,
174- {
175- allowDirtyRead : true ,
176- batchSize : 1 ,
177- }
178- ) ;
179- expect ( cursor . hasNext ) . to . equal ( true ) ;
180- expect ( await cursor . next ( ) ) . to . equal ( 1 ) ;
181- expect ( cursor . hasNext ) . to . equal ( true ) ;
182- expect ( await cursor . next ( ) ) . to . equal ( 2 ) ;
183- } ) ;
184- } ) ;
185-
18623describeIm ( "Cluster round robin" , function ( ) {
18724 this . timeout ( Infinity ) ;
18825 const NUM_COORDINATORS = 3 ;
0 commit comments