Skip to content

Commit 685a2a0

Browse files
authored
Graceful shutdown of the coordinator (#320)
* Graceful shutdown of the coordinator * Add change to CHANGELOG
1 parent 8bd930f commit 685a2a0

File tree

5 files changed

+168
-0
lines changed

5 files changed

+168
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- Add tests for 3.8 ArangoDB and remove tests for 3.5.
77
- Add Plan support in Query execution.
88
- Change Golang version from 1.13.4 to 1.16.6.
9+
- Add graceful shutdown for the coordinators.
910

1011
## [1.1.1](https://github.com/arangodb/go-driver/tree/1.1.1) (2020-11-13)
1112
- Add Driver V2 in Alpha version

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ __test_go_test:
359359
-e TEST_BACKUP_REMOTE_REPO=$(TEST_BACKUP_REMOTE_REPO) \
360360
-e TEST_BACKUP_REMOTE_CONFIG='$(TEST_BACKUP_REMOTE_CONFIG)' \
361361
-e TEST_DEBUG='$(TEST_DEBUG)' \
362+
-e TEST_ENABLE_SHUTDOWN=$(TEST_ENABLE_SHUTDOWN) \
362363
-e GODEBUG=tls13=1 \
363364
-e CGO_ENABLED=$(CGO_ENABLED) \
364365
-w /usr/code/ \
@@ -380,6 +381,7 @@ __test_v2_go_test:
380381
-e TEST_BACKUP_REMOTE_REPO=$(TEST_BACKUP_REMOTE_REPO) \
381382
-e TEST_BACKUP_REMOTE_CONFIG='$(TEST_BACKUP_REMOTE_CONFIG)' \
382383
-e TEST_DEBUG='$(TEST_DEBUG)' \
384+
-e TEST_ENABLE_SHUTDOWN=$(TEST_ENABLE_SHUTDOWN) \
383385
-e GODEBUG=tls13=1 \
384386
-e CGO_ENABLED=$(CGO_ENABLED) \
385387
-w /usr/code/v2/ \

client_server_admin.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ type ClientServerAdmin interface {
4141
// Statistics queries statistics from a specific server
4242
Statistics(ctx context.Context) (ServerStatistics, error)
4343

44+
// ShutdownV2 shuts down a specific coordinator, optionally removing it from the cluster with a graceful manner.
45+
ShutdownV2(ctx context.Context, removeFromCluster, graceful bool) error
46+
47+
// ShutdownInfoV2 queries information about shutdown progress.
48+
ShutdownInfoV2(ctx context.Context) (ShutdownInfo, error)
49+
4450
// Logs retrieve logs from server in ArangoDB 3.8.0+ format
4551
Logs(ctx context.Context) (ServerLogs, error)
4652
}

client_server_admin_impl.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,28 @@ type serverModeRequest struct {
3434
Mode ServerMode `json:"mode"`
3535
}
3636

37+
// ShutdownInfo stores information about shutdown of the coordinator.
38+
type ShutdownInfo struct {
39+
// AQLCursors stores a number of AQL cursors that are still active.
40+
AQLCursors int `json:"AQLcursors"`
41+
// Transactions stores a number of ongoing transactions.
42+
Transactions int `json:"transactions"`
43+
// PendingJobs stores a number of ongoing asynchronous requests.
44+
PendingJobs int `json:"pendingJobs"`
45+
// DoneJobs stores a number of finished asynchronous requests, whose result has not yet been collected.
46+
DoneJobs int `json:"doneJobs"`
47+
// PregelConductors stores a number of ongoing Pregel jobs.
48+
PregelConductors int `json:"pregelConductors"`
49+
// LowPrioOngoingRequests stores a number of ongoing low priority requests.
50+
LowPrioOngoingRequests int `json:"lowPrioOngoingRequests"`
51+
// LowPrioQueuedRequests stores a number of queued low priority requests.
52+
LowPrioQueuedRequests int `json:"lowPrioQueuedRequests"`
53+
// AllClear is set if all operations are closed.
54+
AllClear bool `json:"allClear"`
55+
// SoftShutdownOngoing describes whether a soft shutdown of the Coordinator is in progress.
56+
SoftShutdownOngoing bool `json:"softShutdownOngoing"`
57+
}
58+
3759
// ServerMode returns the current mode in which the server/cluster is operating.
3860
// This call needs ArangoDB 3.3 and up.
3961
func (c *client) ServerMode(ctx context.Context) (ServerMode, error) {
@@ -138,3 +160,48 @@ func (c *client) Statistics(ctx context.Context) (ServerStatistics, error) {
138160
}
139161
return data, nil
140162
}
163+
164+
// ShutdownV2 shuts down a specific coordinator, optionally removing it from the cluster with a graceful manner.
165+
// When `graceful` is true then run soft shutdown process and the `ShutdownInfoV2` can be used to check the progress.
166+
// It is available since versions: v3.7.12, v3.8.1, v3.9.0.
167+
func (c *client) ShutdownV2(ctx context.Context, removeFromCluster, graceful bool) error {
168+
req, err := c.conn.NewRequest("DELETE", "_admin/shutdown")
169+
if err != nil {
170+
return WithStack(err)
171+
}
172+
if removeFromCluster {
173+
req.SetQuery("remove_from_cluster", "1")
174+
}
175+
if graceful {
176+
req.SetQuery("soft", "true")
177+
}
178+
resp, err := c.conn.Do(ctx, req)
179+
if err != nil {
180+
return WithStack(err)
181+
}
182+
if err := resp.CheckStatus(200); err != nil {
183+
return WithStack(err)
184+
}
185+
return nil
186+
}
187+
188+
// ShutdownInfoV2 returns information about shutdown progress.
189+
// It is available since versions: v3.7.12, v3.8.1, v3.9.0.
190+
func (c *client) ShutdownInfoV2(ctx context.Context) (ShutdownInfo, error) {
191+
req, err := c.conn.NewRequest("GET", "_admin/shutdown")
192+
if err != nil {
193+
return ShutdownInfo{}, WithStack(err)
194+
}
195+
resp, err := c.conn.Do(ctx, req)
196+
if err != nil {
197+
return ShutdownInfo{}, WithStack(err)
198+
}
199+
if err := resp.CheckStatus(200); err != nil {
200+
return ShutdownInfo{}, WithStack(err)
201+
}
202+
data := ShutdownInfo{}
203+
if err := resp.ParseBody("", &data); err != nil {
204+
return ShutdownInfo{}, WithStack(err)
205+
}
206+
return data, nil
207+
}

test/server_shutdown_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2021 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Tomasz Mielech
21+
//
22+
23+
package test
24+
25+
import (
26+
"context"
27+
"os"
28+
"testing"
29+
"time"
30+
31+
"github.com/stretchr/testify/require"
32+
)
33+
34+
// TestServerShutdown tests the graceful shutdown on the coordinator.
35+
func TestServerShutdown(t *testing.T) {
36+
enabled := os.Getenv("TEST_ENABLE_SHUTDOWN")
37+
38+
if enabled != "on" && enabled != "1" {
39+
t.Skipf("TEST_ENABLE_SHUTDOWN is not set")
40+
}
41+
42+
c := createClientFromEnv(t, true)
43+
ctx := context.Background()
44+
testing.Short()
45+
46+
// It must be a cluster
47+
versionCheck := EnsureVersion(t, ctx, c).Cluster()
48+
49+
// Check required version.
50+
if !isGracefulShutdownAvailable(versionCheck) {
51+
t.Skipf("Skipping because version %s is not sufficient", versionCheck.version)
52+
}
53+
54+
// Shutdown the coordinator.
55+
err := c.ShutdownV2(ctx, false, true)
56+
require.NoError(t, err, "can not shutdown the coordinator")
57+
58+
// Wait one minute for the coordinator shutdown.
59+
ctxTimeout, _ := context.WithTimeout(ctx, time.Minute)
60+
for {
61+
info, err := c.ShutdownInfoV2(ctxTimeout)
62+
require.NoError(t, err, "can not fetch shutdown progress information")
63+
if info.AllClear {
64+
break
65+
}
66+
require.NoError(t, ctx.Err(), "shutdown coordinator timeout")
67+
}
68+
}
69+
70+
// isGracefulShutdownAvailable returns true since versions: v3.7.12, v3.8.1, v3.9.0.
71+
func isGracefulShutdownAvailable(versionCheck VersionCheck) bool {
72+
if versionCheck.version.Major() > 3 {
73+
return true
74+
}
75+
76+
minor := versionCheck.version.Minor()
77+
if minor < 7 {
78+
return false
79+
}
80+
81+
if minor == 7 {
82+
if versionCheck.version.CompareTo("3.7.12") < 0 {
83+
return false
84+
}
85+
} else if minor == 8 {
86+
if versionCheck.version.CompareTo("3.8.1") < 0 {
87+
return false
88+
}
89+
}
90+
91+
return true
92+
}

0 commit comments

Comments
 (0)