You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

357 lines
13 KiB

//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//
package driver
import (
"context"
"encoding/json"
"fmt"
"reflect"
"time"
)
// Cluster provides access to cluster wide specific operations.
// To use this interface, an ArangoDB cluster is required.
type Cluster interface {
// Get the cluster configuration & health
Health(ctx context.Context) (ClusterHealth, error)
// Get the inventory of the cluster containing all collections (with entire details) of a database.
DatabaseInventory(ctx context.Context, db Database) (DatabaseInventory, error)
// MoveShard moves a single shard of the given collection from server `fromServer` to
// server `toServer`.
MoveShard(ctx context.Context, col Collection, shard ShardID, fromServer, toServer ServerID) error
// CleanOutServer triggers activities to clean out a DBServer.
CleanOutServer(ctx context.Context, serverID string) error
// ResignServer triggers activities to let a DBServer resign for all shards.
ResignServer(ctx context.Context, serverID string) error
// IsCleanedOut checks if the dbserver with given ID has been cleaned out.
IsCleanedOut(ctx context.Context, serverID string) (bool, error)
// RemoveServer is a low-level option to remove a server from a cluster.
// This function is suitable for servers of type coordinator or dbserver.
// The use of `ClientServerAdmin.Shutdown` is highly recommended above this function.
RemoveServer(ctx context.Context, serverID ServerID) error
}
// ServerID identifies an arangod server in a cluster.
type ServerID string
// ClusterHealth contains health information for all servers in a cluster.
type ClusterHealth struct {
// Unique identifier of the entire cluster.
// This ID is created when the cluster was first created.
ID string `json:"ClusterId"`
// Health per server
Health map[ServerID]ServerHealth `json:"Health"`
}
// ServerSyncStatus describes the servers sync status
type ServerSyncStatus string
const (
ServerSyncStatusUnknown ServerSyncStatus = "UNKNOWN"
ServerSyncStatusUndefined ServerSyncStatus = "UNDEFINED"
ServerSyncStatusStartup ServerSyncStatus = "STARTUP"
ServerSyncStatusStopping ServerSyncStatus = "STOPPING"
ServerSyncStatusStopped ServerSyncStatus = "STOPPED"
ServerSyncStatusServing ServerSyncStatus = "SERVING"
ServerSyncStatusShutdown ServerSyncStatus = "SHUTDOWN"
)
// ServerHealth contains health information of a single server in a cluster.
type ServerHealth struct {
Endpoint string `json:"Endpoint"`
LastHeartbeatAcked time.Time `json:"LastHeartbeatAcked"`
LastHeartbeatSent time.Time `json:"LastHeartbeatSent"`
LastHeartbeatStatus string `json:"LastHeartbeatStatus"`
Role ServerRole `json:"Role"`
ShortName string `json:"ShortName"`
Status ServerStatus `json:"Status"`
CanBeDeleted bool `json:"CanBeDeleted"`
HostID string `json:"Host,omitempty"`
Version Version `json:"Version,omitempty"`
Engine EngineType `json:"Engine,omitempty"`
SyncStatus ServerSyncStatus `json:"SyncStatus,omitempty"`
// Only for Coordinators
AdvertisedEndpoint *string `json:"AdvertisedEndpoint,omitempty"`
// Only for Agents
Leader *string `json:"Leader,omitempty"`
Leading *bool `json:"Leading,omitempty"`
}
// ServerStatus describes the health status of a server
type ServerStatus string
const (
// ServerStatusGood indicates server is in good state
ServerStatusGood ServerStatus = "GOOD"
// ServerStatusBad indicates server has missed 1 heartbeat
ServerStatusBad ServerStatus = "BAD"
// ServerStatusFailed indicates server has been declared failed by the supervision, this happens after about 15s being bad.
ServerStatusFailed ServerStatus = "FAILED"
)
// DatabaseInventory describes a detailed state of the collections & shards of a specific database within a cluster.
type DatabaseInventory struct {
// Details of database, this is present since ArangoDB 3.6
Info DatabaseInfo `json:"properties,omitempty"`
// Details of all collections
Collections []InventoryCollection `json:"collections,omitempty"`
// Details of all views
Views []InventoryView `json:"views,omitempty"`
State State `json:"state,omitempty"`
Tick string `json:"tick,omitempty"`
}
type State struct {
Running bool `json:"running,omitempty"`
LastLogTick string `json:"lastLogTick,omitempty"`
LastUncommittedLogTick string `json:"lastUncommittedLogTick,omitempty"`
TotalEvents int64 `json:"totalEvents,omitempty"`
Time time.Time `json:"time,omitempty"`
}
// UnmarshalJSON marshals State to arangodb json representation
func (s *State) UnmarshalJSON(d []byte) error {
var internal interface{}
if err := json.Unmarshal(d, &internal); err != nil {
return err
}
if val, ok := internal.(string); ok {
if val != "unused" {
fmt.Printf("unrecognized State value: %s\n", val)
}
*s = State{}
return nil
} else {
type Alias State
out := Alias{}
if err := json.Unmarshal(d, &out); err != nil {
return &json.UnmarshalTypeError{
Value: string(d),
Type: reflect.TypeOf(s).Elem(),
}
}
*s = State(out)
}
return nil
}
// IsReady returns true if the IsReady flag of all collections is set.
func (i DatabaseInventory) IsReady() bool {
for _, c := range i.Collections {
if !c.IsReady {
return false
}
}
return true
}
// PlanVersion returns the plan version of the first collection in the given inventory.
func (i DatabaseInventory) PlanVersion() int64 {
if len(i.Collections) == 0 {
return 0
}
return i.Collections[0].PlanVersion
}
// CollectionByName returns the InventoryCollection with given name.
// Return false if not found.
func (i DatabaseInventory) CollectionByName(name string) (InventoryCollection, bool) {
for _, c := range i.Collections {
if c.Parameters.Name == name {
return c, true
}
}
return InventoryCollection{}, false
}
// ViewByName returns the InventoryView with given name.
// Return false if not found.
func (i DatabaseInventory) ViewByName(name string) (InventoryView, bool) {
for _, v := range i.Views {
if v.Name == name {
return v, true
}
}
return InventoryView{}, false
}
// InventoryCollection is a single element of a DatabaseInventory, containing all information
// of a specific collection.
type InventoryCollection struct {
Parameters InventoryCollectionParameters `json:"parameters"`
Indexes []InventoryIndex `json:"indexes,omitempty"`
PlanVersion int64 `json:"planVersion,omitempty"`
IsReady bool `json:"isReady,omitempty"`
AllInSync bool `json:"allInSync,omitempty"`
}
// IndexByFieldsAndType returns the InventoryIndex with given fields & type.
// Return false if not found.
func (i InventoryCollection) IndexByFieldsAndType(fields []string, indexType string) (InventoryIndex, bool) {
for _, idx := range i.Indexes {
if idx.Type == indexType && idx.FieldsEqual(fields) {
return idx, true
}
}
return InventoryIndex{}, false
}
// InventoryCollectionParameters contains all configuration parameters of a collection in a database inventory.
type InventoryCollectionParameters struct {
// Available from 3.7 ArangoD version.
CacheEnabled bool `json:"cacheEnabled,omitempty"`
Deleted bool `json:"deleted,omitempty"`
DistributeShardsLike string `json:"distributeShardsLike,omitempty"`
// Deprecated: since 3.7 version. It is related only to MMFiles.
DoCompact bool `json:"doCompact,omitempty"`
// Available from 3.7 ArangoD version.
GloballyUniqueId string `json:"globallyUniqueId,omitempty"`
ID string `json:"id,omitempty"`
// Deprecated: since 3.7 version. It is related only to MMFiles.
IndexBuckets int `json:"indexBuckets,omitempty"`
Indexes []InventoryIndex `json:"indexes,omitempty"`
// Available from 3.9 ArangoD version.
InternalValidatorType int `json:"internalValidatorType,omitempty"`
// Available from 3.7 ArangoD version.
IsDisjoint bool `json:"isDisjoint,omitempty"`
IsSmart bool `json:"isSmart,omitempty"`
// Available from 3.7 ArangoD version.
IsSmartChild bool `json:"isSmartChild,omitempty"`
IsSystem bool `json:"isSystem,omitempty"`
// Deprecated: since 3.7 version. It is related only to MMFiles.
IsVolatile bool `json:"isVolatile,omitempty"`
// Deprecated: since 3.7 version. It is related only to MMFiles.
JournalSize int64 `json:"journalSize,omitempty"`
KeyOptions struct {
AllowUserKeys bool `json:"allowUserKeys,omitempty"`
// Deprecated: this field has wrong type and will be removed in the future. It is not used anymore since it can cause parsing issues.
LastValue int64 `json:"-"`
LastValueV2 uint64 `json:"lastValue,omitempty"`
Type string `json:"type,omitempty"`
} `json:"keyOptions"`
// Deprecated: use 'WriteConcern' instead.
MinReplicationFactor int `json:"minReplicationFactor,omitempty"`
Name string `json:"name,omitempty"`
NumberOfShards int `json:"numberOfShards,omitempty"`
// Deprecated: since 3.7 ArangoD version.
Path string `json:"path,omitempty"`
PlanID string `json:"planId,omitempty"`
ReplicationFactor int `json:"replicationFactor,omitempty"`
// Schema for collection validation.
Schema *CollectionSchemaOptions `json:"schema,omitempty"`
ShadowCollections []int `json:"shadowCollections,omitempty"`
ShardingStrategy ShardingStrategy `json:"shardingStrategy,omitempty"`
ShardKeys []string `json:"shardKeys,omitempty"`
Shards map[ShardID][]ServerID `json:"shards,omitempty"`
// Optional only for some collections.
SmartGraphAttribute string `json:"smartGraphAttribute,omitempty"`
// Optional only for some collections.
SmartJoinAttribute string `json:"smartJoinAttribute,omitempty"`
Status CollectionStatus `json:"status,omitempty"`
// Available from 3.7 ArangoD version.
SyncByRevision bool `json:"syncByRevision,omitempty"`
Type CollectionType `json:"type,omitempty"`
// Available from 3.7 ArangoD version.
UsesRevisionsAsDocumentIds bool `json:"usesRevisionsAsDocumentIds,omitempty"`
WaitForSync bool `json:"waitForSync,omitempty"`
// Available from 3.6 ArangoD version.
WriteConcern int `json:"writeConcern,omitempty"`
// Available from 3.10 ArangoD version.
ComputedValues []ComputedValue `json:"computedValues,omitempty"`
}
// IsSatellite returns true if the collection is a satellite collection
func (icp *InventoryCollectionParameters) IsSatellite() bool {
return icp.ReplicationFactor == ReplicationFactorSatellite
}
// ShardID is an internal identifier of a specific shard
type ShardID string
// InventoryIndex contains all configuration parameters of a single index of a collection in a database inventory.
type InventoryIndex struct {
ID string `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Fields []string `json:"fields,omitempty"`
Unique bool `json:"unique"`
Sparse bool `json:"sparse"`
Deduplicate bool `json:"deduplicate"`
MinLength int `json:"minLength,omitempty"`
GeoJSON bool `json:"geoJson,omitempty"`
Name string `json:"name,omitempty"`
ExpireAfter int `json:"expireAfter,omitempty"`
Estimates bool `json:"estimates,omitempty"`
FieldValueTypes string `json:"fieldValueTypes,omitempty"`
CacheEnabled *bool `json:"cacheEnabled,omitempty"`
}
// FieldsEqual returns true when the given fields list equals the
// Fields list in the InventoryIndex.
// The order of fields is irrelevant.
func (i InventoryIndex) FieldsEqual(fields []string) bool {
return stringSliceEqualsIgnoreOrder(i.Fields, fields)
}
// InventoryView is a single element of a DatabaseInventory, containing all information
// of a specific view.
type InventoryView struct {
Name string `json:"name,omitempty"`
Deleted bool `json:"deleted,omitempty"`
ID string `json:"id,omitempty"`
IsSystem bool `json:"isSystem,omitempty"`
PlanID string `json:"planId,omitempty"`
Type ViewType `json:"type,omitempty"`
// Include all properties from an arangosearch view.
ArangoSearchViewProperties
}
// stringSliceEqualsIgnoreOrder returns true when the given lists contain the same elements.
// The order of elements is irrelevant.
func stringSliceEqualsIgnoreOrder(a, b []string) bool {
if len(a) != len(b) {
return false
}
bMap := make(map[string]struct{})
for _, x := range b {
bMap[x] = struct{}{}
}
for _, x := range a {
if _, found := bMap[x]; !found {
return false
}
}
return true
}