Repository: prometheus/prometheus
File: util/teststorage/appender.go (Go)

Code before:

	next storage.AppenderV2
}

func (a *Appendable) AppenderV2(ctx context.Context) storage.AppenderV2 {
	ret := &appenderV2{appender: appender{a: a}}
	if a.openAppenders.Inc() > 1 {
		ret.err = errors.New("teststorage.Appendable.AppenderV2() concurrent use is not supported; attempted opening new AppenderV2() without Commit/Rollback of the previous one. Extend the implementation if concurrent mock is needed")
	}

	if a.nextV2 != nil {
		ret.next = a.nextV2.AppenderV2(ctx)
	} else if a.next != nil {
		ret.err = errors.Join(ret.err, errors.New("teststorage.Appendable.AppenderV2() invoked with .Then but no .ThenV2 was supplied; likely bug"))
	}
	return ret
}

func (a *appenderV2) Append(ref storage.SeriesRef, ls labels.Labels, st, t int64, v float64, h *histogram.Histogram, fh *histogram.FloatHistogram, opts storage.AOptions) (_ storage.SeriesRef, err error) {
	if err := a.checkErr(); err != nil {
		return 0, err
	}

	if a.a.appendErrFn != nil {
		if err := a.a.appendErrFn(ls); err != nil {
			return 0, err
		}
	}

	a.a.mtx.Lock()
	var es []exemplar.Exemplar
	if len(opts.Exemplars) > 0 {
		// As per AppenderV2 interface, opts.Exemplar slice is unsafe for reuse.
		es = make([]exemplar.Exemplar, len(opts.Exemplars))
		copy(es, opts.Exemplars)
	}
	a.a.pendingSamples = append(a.a.pendingSamples, Sample{
		MF: opts.MetricFamilyName,
		M:  opts.Metadata,
		L:  ls,
		ST: st, T: t,
		V: v, H: h, FH: fh,
		ES: es,
	})
	a.a.mtx.Unlock()

	var partialErr error
	if a.a.appendExemplarsError != nil {
		var exErrs []error
		for range opts.Exemplars {
			exErrs = append(exErrs, a.a.appendExemplarsError)

Review comment (suggestion):
Shouldn't you implement `appenderV2.Commit` and `appenderV2.Rollback`?
```suggestion

func (a *appenderV2) Commit() error {
	if err := a.appender.Commit(); err != nil {
		return err
	}
	if a.next != nil {
		return a.next.Commit()
	}
	return nil
}

func (a *appenderV2) Rollback() error {
	if err := a.appender.Rollback(); err != nil {
		return err
	}
	if a.next != nil {
		return a.next.Rollback()
	}
	return nil
}
```

Code after:
		return a.next.UpdateMetadata(ref, l, m)
	}
	return computeOrCheckRef(ref, l)
}

type appenderV2 struct {
	baseAppender

	next storage.AppenderV2
}

func (a *Appendable) AppenderV2(ctx context.Context) storage.AppenderV2 {
	ret := &appenderV2{baseAppender: baseAppender{a: a}}
	if a.openAppenders.Inc() > 1 {
		ret.err = errors.New("teststorage.Appendable.AppenderV2() concurrent use is not supported; attempted opening new AppenderV2() without Commit/Rollback of the previous one. Extend the implementation if concurrent mock is needed")
	}

	if a.nextV2 != nil {
		app := a.nextV2.AppenderV2(ctx)
		ret.next, ret.nextTr = app, app
	} else if a.next != nil {
		ret.err = errors.Join(ret.err, errors.New("teststorage.Appendable.AppenderV2() invoked with .Then but no .ThenV2 was supplied; likely bug"))
	}
	return ret
}

func (a *appenderV2) Append(ref storage.SeriesRef, ls labels.Labels, st, t int64, v float64, h *histogram.Histogram, fh *histogram.FloatHistogram, opts storage.AOptions) (_ storage.SeriesRef, err error) {
	if err := a.checkErr(); err != nil {
		return 0, err
	}

	if a.a.appendErrFn != nil {
		if err := a.a.appendErrFn(ls); err != nil {
			return 0, err
		}
	}

	a.a.mtx.Lock()
	var es []exemplar.Exemplar
	if len(opts.Exemplars) > 0 {
		// As per AppenderV2 interface, opts.Exemplar slice is unsafe for reuse.
		es = make([]exemplar.Exemplar, len(opts.Exemplars))
		copy(es, opts.Exemplars)
	}
	a.a.pendingSamples = append(a.a.pendingSamples, Sample{
		MF: opts.MetricFamilyName,
		M:  opts.Metadata,
		L:  ls,
		ST: st, T: t,
		V: v, H: h, FH: fh,
		ES: es,