1// Copyright 2013-2020 Aerospike, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package types
16
17import (
18	"errors"
19	"strings"
20)
21
22// AerospikeError implements error interface for aerospike specific errors.
23// All errors returning from the library are of this type.
24// Errors resulting from Go's stdlib are not translated to this type, unless
25// they are a net.Timeout error.
26type AerospikeError struct {
27	error
28
29	resultCode ResultCode
30	inDoubt    bool
31}
32
33// ResultCode returns the ResultCode from AerospikeError object.
34func (ase AerospikeError) ResultCode() ResultCode {
35	return ase.resultCode
36}
37
38// InDoubt determines if a write transaction may have completed or not.
39func (ase AerospikeError) InDoubt() bool {
40	return ase.inDoubt
41}
42
43// SetInDoubt sets whether it is possible that the write transaction may have completed
44// even though this error was generated.  This may be the case when a
45// client error occurs (like timeout) after the command was sent to the server.
46func (ase *AerospikeError) SetInDoubt(isRead bool, commandSentCounter int) {
47	if !isRead && (commandSentCounter > 1 || (commandSentCounter == 1 && (ase.resultCode == TIMEOUT || ase.resultCode <= 0))) {
48		ase.inDoubt = true
49	}
50}
51
52// MarkInDoubt marks an error as in doubt.
53func (ase *AerospikeError) MarkInDoubt() {
54	ase.inDoubt = true
55}
56
57// NewAerospikeError generates a new AerospikeError instance.
58// If no message is provided, the result code will be translated into the default
59// error message automatically.
60// To be able to check for error type, you could use the following:
61//   if aerr, ok := err.(AerospikeError); ok {
62//       errCode := aerr.ResultCode()
63//       errMessage := aerr.Error()
64//   }
65func NewAerospikeError(code ResultCode, messages ...string) error {
66	if len(messages) == 0 {
67		messages = []string{ResultCodeToString(code)}
68	}
69
70	err := errors.New(strings.Join(messages, " "))
71	return AerospikeError{error: err, resultCode: code}
72}
73
74var ErrServerNotAvailable = NewAerospikeError(SERVER_NOT_AVAILABLE)
75var ErrKeyNotFound = NewAerospikeError(KEY_NOT_FOUND_ERROR)
76var ErrRecordsetClosed = NewAerospikeError(RECORDSET_CLOSED)
77var ErrConnectionPoolEmpty = NewAerospikeError(NO_AVAILABLE_CONNECTIONS_TO_NODE, "Connection pool is empty. This happens when either all connection are in-use already, or no connections were available")
78var ErrTooManyConnectionsForNode = NewAerospikeError(NO_AVAILABLE_CONNECTIONS_TO_NODE, "Connection limit reached for this node. This value is controlled via ClientPolicy.LimitConnectionsToQueueSize")
79var ErrTooManyOpeningConnections = NewAerospikeError(NO_AVAILABLE_CONNECTIONS_TO_NODE, "Too many connections are trying to open at once. This value is controlled via ClientPolicy.OpeningConnectionThreshold")
80var ErrTimeout = NewAerospikeError(TIMEOUT, "command execution timed out on client: See `Policy.Timeout`")
81var ErrUDFBadResponse = NewAerospikeError(UDF_BAD_RESPONSE, "Invalid UDF return value")
82var ErrNoOperationsSpecified = NewAerospikeError(INVALID_COMMAND, "No operations were passed to QueryExecute")
83var ErrNoBinNamesAlloedInQueryExecute = NewAerospikeError(INVALID_COMMAND, "Statement.BinNames must be empty for QueryExecute")
84var ErrFilteredOut = NewAerospikeError(FILTERED_OUT)
85