// Copyright 2015 The Prometheus Authors
// 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.

package storage

import (
	"github.com/prometheus/common/model"
)

// SampleAppender is the interface to append samples to both, local and remote
// storage. All methods are goroutine-safe.
type SampleAppender interface {
	// Append appends a sample to the underlying storage. Depending on the
	// storage implementation, there are different guarantees for the fate
	// of the sample after Append has returned. Remote storage
	// implementation will simply drop samples if they cannot keep up with
	// sending samples. Local storage implementations will only drop metrics
	// upon unrecoverable errors.
	Append(*model.Sample) error
	// NeedsThrottling returns true if the underlying storage wishes to not
	// receive any more samples. Append will still work but might lead to
	// undue resource usage. It is recommended to call NeedsThrottling once
	// before an upcoming batch of Append calls (e.g. a full scrape of a
	// target or the evaluation of a rule group) and only proceed with the
	// batch if NeedsThrottling returns false. In that way, the result of a
	// scrape or of an evaluation of a rule group will always be appended
	// completely or not at all, and the work of scraping or evaluation will
	// not be performed in vain. Also, a call of NeedsThrottling is
	// potentially expensive, so limiting the number of calls is reasonable.
	//
	// Only SampleAppenders for which it is considered critical to receive
	// each and every sample should ever return true. SampleAppenders that
	// tolerate not receiving all samples should always return false and
	// instead drop samples as they see fit to avoid overload.
	NeedsThrottling() bool
}

// Fanout is a SampleAppender that appends every sample to each SampleAppender
// in its list.
type Fanout []SampleAppender

// Append implements SampleAppender. It appends the provided sample to all
// SampleAppenders in the Fanout slice and waits for each append to complete
// before proceeding with the next.
// If any of the SampleAppenders returns an error, the first one is returned
// at the end.
func (f Fanout) Append(s *model.Sample) error {
	var err error
	for _, a := range f {
		if e := a.Append(s); e != nil && err == nil {
			err = e
		}
	}
	return err
}

// NeedsThrottling returns true if at least one of the SampleAppenders in the
// Fanout slice is throttled.
func (f Fanout) NeedsThrottling() bool {
	for _, a := range f {
		if a.NeedsThrottling() {
			return true
		}
	}
	return false
}