diff --git a/exporter/exporterhelper/internal/queuebatch/partition_batcher.go b/exporter/exporterhelper/internal/queuebatch/partition_batcher.go index 998815518ba3..515f2232736b 100644 --- a/exporter/exporterhelper/internal/queuebatch/partition_batcher.go +++ b/exporter/exporterhelper/internal/queuebatch/partition_batcher.go @@ -5,7 +5,6 @@ package queuebatch // import "go.opentelemetry.io/collector/exporter/exporterhel import ( "context" - "strings" "sync" "time" @@ -69,12 +68,8 @@ func (qb *partitionBatcher) Consume(ctx context.Context, req request.Request, do if qb.currentBatch == nil { reqList, mergeSplitErr := req.MergeSplit(ctx, int(qb.cfg.MaxSize), qb.cfg.Sizer, nil) if mergeSplitErr != nil { - qb.logger.Warn(mergeSplitErr.Error()) - if !strings.Contains(mergeSplitErr.Error(), "partial success") { - done.OnDone(mergeSplitErr) - qb.currentBatchMu.Unlock() - return - } + // Do not return in case of error if there are data, try to export as much as possible. + qb.logger.Warn("Failed to split request.", zap.Error(mergeSplitErr)) } if len(reqList) == 0 { @@ -84,8 +79,16 @@ func (qb *partitionBatcher) Consume(ctx context.Context, req request.Request, do } // If more than one flush is required for this request, call done only when all flushes are done. - if len(reqList) > 1 { - done = newRefCountDone(done, int64(len(reqList))) + numRefs := len(reqList) + // Need to also inform about the mergeSplitErr, consider the errored data as 1 batch. + if mergeSplitErr != nil { + numRefs++ + } + if numRefs > 1 { + done = newRefCountDone(done, int64(numRefs)) + if mergeSplitErr != nil { + done.OnDone(mergeSplitErr) + } } // We have at least one result in the reqList. Last in the list may not have enough data to be flushed. @@ -113,12 +116,8 @@ func (qb *partitionBatcher) Consume(ctx context.Context, req request.Request, do reqList, mergeSplitErr := qb.currentBatch.req.MergeSplit(ctx, int(qb.cfg.MaxSize), qb.cfg.Sizer, req) // If failed to merge signal all Done callbacks from the current batch as well as the current request and reset the current batch. if mergeSplitErr != nil { - qb.logger.Warn(mergeSplitErr.Error()) - if !strings.Contains(mergeSplitErr.Error(), "partial success") { - done.OnDone(mergeSplitErr) - qb.currentBatchMu.Unlock() - return - } + // Do not return in case of error if there are data, try to export as much as possible. + qb.logger.Warn("Failed to split request.", zap.Error(mergeSplitErr)) } if len(reqList) == 0 { @@ -128,8 +127,16 @@ func (qb *partitionBatcher) Consume(ctx context.Context, req request.Request, do } // If more than one flush is required for this request, call done only when all flushes are done. - if len(reqList) > 1 { + numRefs := len(reqList) + // Need to also inform about the mergeSplitErr, consider the errored data as 1 batch. + if mergeSplitErr != nil { + numRefs++ + } + if numRefs > 1 { done = newRefCountDone(done, int64(len(reqList))) + if mergeSplitErr != nil { + done.OnDone(mergeSplitErr) + } } // We have at least one result in the reqList, if more results here is what that means: diff --git a/exporter/exporterhelper/internal/queuebatch/partition_batcher_test.go b/exporter/exporterhelper/internal/queuebatch/partition_batcher_test.go index 7e33323a7edd..10faa7ed4193 100644 --- a/exporter/exporterhelper/internal/queuebatch/partition_batcher_test.go +++ b/exporter/exporterhelper/internal/queuebatch/partition_batcher_test.go @@ -6,7 +6,6 @@ package queuebatch import ( "context" "errors" - "fmt" "runtime" "sync/atomic" "testing" @@ -382,33 +381,12 @@ func TestPartitionBatcher_MergeError(t *testing.T) { assert.EqualValues(t, 0, done.success.Load()) } -type customPartialErrorRequest struct { - *requesttest.FakeRequest - failureCount int - maxSize int -} - -func (r *customPartialErrorRequest) MergeSplit(_ context.Context, _ int, _ request.SizerType, _ request.Request) ([]request.Request, error) { - return nil, fmt.Errorf("partial success: failed to split request: size is greater than max size. size: %d, max_size: %d. Failed: %d", - r.ItemsCount(), - r.maxSize, - r.ItemsCount(), - ) -} - -func (r *customPartialErrorRequest) ItemsCount() int { - return r.FakeRequest.ItemsCount() -} - -func (r *customPartialErrorRequest) OnError(err error) request.Request { - return r.FakeRequest.OnError(err) -} - -func TestShardBatcher_PartialSuccessError(t *testing.T) { +func TestPartitionBatcher_PartialSuccessError(t *testing.T) { cfg := BatchConfig{ FlushTimeout: 0, - MinSize: 0, - MaxSize: 10, + Sizer: request.SizerTypeBytes, + MinSize: 10, + MaxSize: 15, } core, observed := observer.New(zap.WarnLevel) @@ -416,19 +394,13 @@ func TestShardBatcher_PartialSuccessError(t *testing.T) { sink := requesttest.NewSink() ba := newPartitionBatcher(cfg, request.NewItemsSizer(), newWorkerPool(1), sink.Export, logger) require.NoError(t, ba.Start(context.Background(), componenttest.NewNopHost())) - t.Cleanup(func() { - require.NoError(t, ba.Shutdown(context.Background())) - }) done := newFakeDone() - - req := &customPartialErrorRequest{ - maxSize: 10, - FakeRequest: &requesttest.FakeRequest{ - Items: 100, - Bytes: 100, - }, - failureCount: 42, + req := &requesttest.FakeRequest{ + Items: 100, + Bytes: 100, + MergeErr: errors.New("split error"), + MergeErrResult: []request.Request{&requesttest.FakeRequest{Items: 10, Bytes: 15}}, } ba.Consume(context.Background(), req, done) @@ -439,91 +411,60 @@ func TestShardBatcher_PartialSuccessError(t *testing.T) { } log := logs[0] return log.Level == zap.WarnLevel && - log.Message == "partial success: failed to split request: size is greater than max size. size: 100, max_size: 10. Failed: 100" + log.Message == "Failed to split request." }, time.Second, 10*time.Millisecond) - // Verify that done callback was called with the error - assert.Eventually(t, func() bool { - return done.errors.Load() == 1 - }, time.Second, 10*time.Millisecond) + require.NoError(t, ba.Shutdown(context.Background())) + + // Verify that done callback was called with the returned batch and error for the split. + assert.Equal(t, int64(1), done.errors.Load()) + assert.Equal(t, 1, sink.RequestsCount()) + assert.Equal(t, 10, sink.ItemsCount()) + assert.Equal(t, 15, sink.BytesCount()) } -func TestShardBatcher_PartialSuccessError_WithLogs(t *testing.T) { +func TestSPartitionBatcher_PartialSuccessError_AfterOkRequest(t *testing.T) { cfg := BatchConfig{ FlushTimeout: 0, + Sizer: request.SizerTypeBytes, MinSize: 10, + MaxSize: 15, } - core, logs := observer.New(zap.WarnLevel) + core, observed := observer.New(zap.WarnLevel) logger := zap.New(core) - sink := requesttest.NewSink() ba := newPartitionBatcher(cfg, request.NewItemsSizer(), newWorkerPool(1), sink.Export, logger) require.NoError(t, ba.Start(context.Background(), componenttest.NewNopHost())) - t.Cleanup(func() { - require.NoError(t, ba.Shutdown(context.Background())) - }) done := newFakeDone() - req := &customPartialErrorRequest{ - FakeRequest: &requesttest.FakeRequest{ - Items: 8, - MergeErr: errors.New("partial success: failed to split request: size is greater than max size. size: 3, max_size: 10. Failed: 3"), - }, - failureCount: 42, + ba.Consume(context.Background(), &requesttest.FakeRequest{Items: 5, Bytes: 5}, done) + req := &requesttest.FakeRequest{ + Items: 100, + Bytes: 100, + MergeErr: errors.New("split error"), + MergeErrResult: []request.Request{&requesttest.FakeRequest{Items: 10, Bytes: 15}}, } - ba.Consume(context.Background(), req, done) assert.Eventually(t, func() bool { - return done.errors.Load() == 1 - }, 1*time.Second, 10*time.Millisecond) - - allLogs := logs.All() - require.Len(t, allLogs, 1) - assert.Contains(t, allLogs[0].Message, "partial success: failed to split request:") -} - -func TestShardBatcher_PartialSuccessError_WithFailureCountAndReason(t *testing.T) { - core, observedLogs := observer.New(zap.WarnLevel) - logger := zap.New(core) - - cfg := BatchConfig{ - FlushTimeout: 0, - MinSize: 5, - MaxSize: 10, - } - - sink := requesttest.NewSink() - ba := newPartitionBatcher(cfg, request.NewItemsSizer(), newWorkerPool(1), sink.Export, logger) - require.NoError(t, ba.Start(context.Background(), componenttest.NewNopHost())) - t.Cleanup(func() { - require.NoError(t, ba.Shutdown(context.Background())) - }) - - // First add a request to create a current batch - done1 := newFakeDone() - req1 := &requesttest.FakeRequest{ - Items: 3, - } - ba.Consume(context.Background(), req1, done1) - - // Now add a request that will trigger the partial success error - done2 := newFakeDone() - req2 := &requesttest.FakeRequest{ - Items: 3, - MergeErr: errors.New("partial success: failed to split request: size is greater than max size. size: 3, max_size: 10. Failed: 3"), - } - ba.Consume(context.Background(), req2, done2) - - assert.Eventually(t, func() bool { - return observedLogs.Len() > 0 + logs := observed.All() + if len(logs) == 0 { + return false + } + log := logs[0] + return log.Level == zap.WarnLevel && + log.Message == "Failed to split request." }, time.Second, 10*time.Millisecond) - allLogs := observedLogs.All() - require.Len(t, allLogs, 1) - assert.Contains(t, allLogs[0].Message, "failed to split request") - assert.EqualValues(t, 1, done2.errors.Load()) + require.NoError(t, ba.Shutdown(context.Background())) + + // Verify that done callback was called with the success for the returned batch and error for the split. + assert.Equal(t, int64(1), done.errors.Load()) + assert.Equal(t, int64(1), done.success.Load()) + assert.Equal(t, 1, sink.RequestsCount()) + assert.Equal(t, 10, sink.ItemsCount()) + assert.Equal(t, 15, sink.BytesCount()) } type fakeDone struct { diff --git a/exporter/exporterhelper/internal/requesttest/request.go b/exporter/exporterhelper/internal/requesttest/request.go index cc764be58ec3..135f1cb77ab8 100644 --- a/exporter/exporterhelper/internal/requesttest/request.go +++ b/exporter/exporterhelper/internal/requesttest/request.go @@ -24,11 +24,12 @@ func (e errorPartial) Error() string { } type FakeRequest struct { - Items int - Bytes int - Partial int - MergeErr error - Delay time.Duration + Items int + Bytes int + Partial int + MergeErr error + MergeErrResult []request.Request + Delay time.Duration } func (r *FakeRequest) OnError(err error) request.Request { @@ -45,13 +46,13 @@ func (r *FakeRequest) ItemsCount() int { func (r *FakeRequest) MergeSplit(_ context.Context, maxSize int, szt request.SizerType, r2 request.Request) ([]request.Request, error) { if r.MergeErr != nil { - return nil, r.MergeErr + return r.MergeErrResult, r.MergeErr } if r2 != nil { fr2 := r2.(*FakeRequest) if fr2.MergeErr != nil { - return nil, fr2.MergeErr + return fr2.MergeErrResult, fr2.MergeErr } fr2.mergeTo(r) } diff --git a/exporter/exporterhelper/logs_batch.go b/exporter/exporterhelper/logs_batch.go index 1c0b018ebe34..08b8664ee787 100644 --- a/exporter/exporterhelper/logs_batch.go +++ b/exporter/exporterhelper/logs_batch.go @@ -50,29 +50,14 @@ func (req *logsRequest) mergeTo(dst *logsRequest, sz sizer.LogsSizer) { func (req *logsRequest) split(maxSize int, sz sizer.LogsSizer) ([]Request, error) { var res []Request - var ld plog.Logs - rmSize := -1 - - previousSize := req.size(sz) - - for req.size(sz) > maxSize && rmSize != 0 { - ld, rmSize = extractLogs(req.ld, maxSize, sz) - if ld.LogRecordCount() > 0 { - req.setCachedSize(req.size(sz) - rmSize) - res = append(res, newLogsRequest(ld)) + for req.size(sz) > maxSize { + ld, removedSize := extractLogs(req.ld, maxSize, sz) + if ld.LogRecordCount() == 0 { + return res, fmt.Errorf("one log record size is greater than max size, dropping items: %d", req.ld.LogRecordCount()) } + req.setCachedSize(req.size(sz) - removedSize) + res = append(res, newLogsRequest(ld)) } - - if req.size(sz) == previousSize && req.size(sz) > maxSize { - err := fmt.Errorf( - "partial success: failed to split logs request: size is greater than max size. size: %d, max_size: %d. Failed: %d", - req.size(sz), - maxSize, - req.ld.LogRecordCount(), - ) - return res, err - } - res = append(res, req) return res, nil } diff --git a/exporter/exporterhelper/logs_batch_test.go b/exporter/exporterhelper/logs_batch_test.go index 782266b31c3a..8406b99a657d 100644 --- a/exporter/exporterhelper/logs_batch_test.go +++ b/exporter/exporterhelper/logs_batch_test.go @@ -233,16 +233,33 @@ func TestMergeSplitLogsBasedOnByteSize(t *testing.T) { expected: []Request{}, expectPartialError: true, }, + { + name: "splittable_then_unsplittable_log", + szt: RequestSizerTypeBytes, + maxSize: 1000, + lr1: newLogsRequest(func() plog.Logs { + ld := testdata.GenerateLogs(2) + ld.ResourceLogs().At(0).ScopeLogs().At(0).LogRecords().At(0).Body().SetStr(string(make([]byte, 10))) + ld.ResourceLogs().At(0).ScopeLogs().At(0).LogRecords().At(1).Body().SetStr(string(make([]byte, 1001))) + return ld + }()), + lr2: nil, + expected: []Request{newLogsRequest(func() plog.Logs { + ld := testdata.GenerateLogs(1) + ld.ResourceLogs().At(0).ScopeLogs().At(0).LogRecords().At(0).Body().SetStr(string(make([]byte, 10))) + return ld + }())}, + expectPartialError: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { res, err := tt.lr1.MergeSplit(context.Background(), tt.maxSize, tt.szt, tt.lr2) if tt.expectPartialError { - require.Error(t, err) - assert.Contains(t, err.Error(), "partial success: failed to split logs request: size is greater than max size") - return + require.ErrorContains(t, err, "one log record size is greater than max size, dropping") + } else { + require.NoError(t, err) } - require.NoError(t, err) assert.Len(t, res, len(tt.expected)) for i := range res { assert.Equal(t, tt.expected[i].(*logsRequest).ld, res[i].(*logsRequest).ld) diff --git a/exporter/exporterhelper/metrics_batch.go b/exporter/exporterhelper/metrics_batch.go index 391c6202ac09..34a6449a61bb 100644 --- a/exporter/exporterhelper/metrics_batch.go +++ b/exporter/exporterhelper/metrics_batch.go @@ -50,27 +50,13 @@ func (req *metricsRequest) mergeTo(dst *metricsRequest, sz sizer.MetricsSizer) { func (req *metricsRequest) split(maxSize int, sz sizer.MetricsSizer) ([]Request, error) { var res []Request - var md pmetric.Metrics - rmSize := -1 - - previousSize := req.size(sz) - - for req.size(sz) > maxSize && rmSize != 0 { - md, rmSize = extractMetrics(req.md, maxSize, sz) - if md.DataPointCount() > 0 { - req.setCachedSize(req.size(sz) - rmSize) - res = append(res, newMetricsRequest(md)) + for req.size(sz) > maxSize { + md, rmSize := extractMetrics(req.md, maxSize, sz) + if md.DataPointCount() == 0 { + return res, fmt.Errorf("one datapoint size is greater than max size, dropping items: %d", req.md.DataPointCount()) } - } - - if req.size(sz) == previousSize && req.size(sz) > maxSize { - err := fmt.Errorf( - "partial success: failed to split metrics request: size is greater than max size. size: %d, max_size: %d. Failed: %d", - req.size(sz), - maxSize, - req.md.MetricCount(), - ) - return res, err + req.setCachedSize(req.size(sz) - rmSize) + res = append(res, newMetricsRequest(md)) } res = append(res, req) return res, nil diff --git a/exporter/exporterhelper/metrics_batch_test.go b/exporter/exporterhelper/metrics_batch_test.go index 5e5e97311f4f..c75a6e367b0b 100644 --- a/exporter/exporterhelper/metrics_batch_test.go +++ b/exporter/exporterhelper/metrics_batch_test.go @@ -433,12 +433,30 @@ func TestMergeSplitMetricsBasedOnByteSize(t *testing.T) { expected: []Request{}, expectSplitError: true, }, + { + name: "splittable_then_unsplittable_metric", + szt: RequestSizerTypeBytes, + maxSize: 1000, + mr1: newMetricsRequest(func() pmetric.Metrics { + md := testdata.GenerateMetrics(2) + md.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics().At(0).SetDescription(string(make([]byte, 10))) + md.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics().At(1).SetDescription(string(make([]byte, 1001))) + return md + }()), + mr2: nil, + expected: []Request{newMetricsRequest(func() pmetric.Metrics { + md := testdata.GenerateMetrics(1) + md.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics().At(0).SetDescription(string(make([]byte, 10))) + return md + }())}, + expectSplitError: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { res, err := tt.mr1.MergeSplit(context.Background(), tt.maxSize, tt.szt, tt.mr2) if tt.expectSplitError { - require.ErrorContains(t, err, "partial success: failed to split metrics request: size is greater than max size") + require.ErrorContains(t, err, "one datapoint size is greater than max size, dropping items:") } else { require.NoError(t, err) } diff --git a/exporter/exporterhelper/traces_batch.go b/exporter/exporterhelper/traces_batch.go index 9ccc422c47ed..4c6d9c1dccf3 100644 --- a/exporter/exporterhelper/traces_batch.go +++ b/exporter/exporterhelper/traces_batch.go @@ -50,29 +50,14 @@ func (req *tracesRequest) mergeTo(dst *tracesRequest, sz sizer.TracesSizer) { func (req *tracesRequest) split(maxSize int, sz sizer.TracesSizer) ([]Request, error) { var res []Request - var td ptrace.Traces - rmSize := -1 - - previousSize := req.size(sz) - - for req.size(sz) > maxSize && rmSize != 0 { - td, rmSize = extractTraces(req.td, maxSize, sz) - if td.SpanCount() > 0 { - req.setCachedSize(req.size(sz) - rmSize) - res = append(res, newTracesRequest(td)) + for req.size(sz) > maxSize { + td, rmSize := extractTraces(req.td, maxSize, sz) + if td.SpanCount() == 0 { + return res, fmt.Errorf("one span size is greater than max size, dropping items: %d", req.td.SpanCount()) } + req.setCachedSize(req.size(sz) - rmSize) + res = append(res, newTracesRequest(td)) } - - if req.size(sz) == previousSize && req.size(sz) > maxSize { - err := fmt.Errorf( - "partial success: failed to split traces request: size is greater than max size. size: %d, max_size: %d. Failed: %d", - req.size(sz), - maxSize, - req.td.SpanCount(), - ) - return res, err - } - res = append(res, req) return res, nil } diff --git a/exporter/exporterhelper/traces_batch_test.go b/exporter/exporterhelper/traces_batch_test.go index 2cb0cff5d2fe..71b42bc0fb16 100644 --- a/exporter/exporterhelper/traces_batch_test.go +++ b/exporter/exporterhelper/traces_batch_test.go @@ -246,16 +246,33 @@ func TestMergeSplitTracesBasedOnByteSize(t *testing.T) { expected: []Request{}, expectPartialError: true, }, + { + name: "splittable_then_unsplittable_trace", + szt: RequestSizerTypeBytes, + maxSize: 1000, + lr1: newTracesRequest(func() ptrace.Traces { + ld := testdata.GenerateTraces(2) + ld.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).Attributes().PutStr("large_attr", string(make([]byte, 10))) + ld.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(1).Attributes().PutStr("large_attr", string(make([]byte, 1001))) + return ld + }()), + lr2: nil, + expected: []Request{newTracesRequest(func() ptrace.Traces { + ld := testdata.GenerateTraces(1) + ld.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).Attributes().PutStr("large_attr", string(make([]byte, 10))) + return ld + }())}, + expectPartialError: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { res, err := tt.lr1.MergeSplit(context.Background(), tt.maxSize, tt.szt, tt.lr2) if tt.expectPartialError { - require.Error(t, err) - assert.Contains(t, err.Error(), "partial success: failed to split traces request: size is greater than max size") - return + require.ErrorContains(t, err, "one span size is greater than max size, dropping items:") + } else { + require.NoError(t, err) } - require.NoError(t, err) assert.Len(t, res, len(tt.expected)) for i := range res { assert.Equal(t, tt.expected[i].(*tracesRequest).td, res[i].(*tracesRequest).td) diff --git a/exporter/exporterhelper/xexporterhelper/profiles_batch.go b/exporter/exporterhelper/xexporterhelper/profiles_batch.go index 1356089efd05..829fca166185 100644 --- a/exporter/exporterhelper/xexporterhelper/profiles_batch.go +++ b/exporter/exporterhelper/xexporterhelper/profiles_batch.go @@ -6,6 +6,7 @@ package xexporterhelper // import "go.opentelemetry.io/collector/exporter/export import ( "context" "errors" + "fmt" "go.opentelemetry.io/collector/exporter/exporterhelper" "go.opentelemetry.io/collector/exporter/exporterhelper/internal/sizer" @@ -40,14 +41,17 @@ func (req *profilesRequest) MergeSplit(_ context.Context, maxSize int, szt expor return []exporterhelper.Request{req, req2}, nil } - return append(req.split(maxSize, sz), req2.split(maxSize, sz)...), nil + sp1, err1 := req.split(maxSize, sz) + sp2, err2 := req2.split(maxSize, sz) + + return append(sp1, sp2...), errors.Join(err1, err2) } // If no limit we can simply merge the new request into the current and return. if maxSize == 0 { return []exporterhelper.Request{req}, nil } - return req.split(maxSize, sz), nil + return req.split(maxSize, sz) } // TODO(13106): handle merging of profiles (and change the indice tables with their new indices) @@ -59,15 +63,19 @@ func (req *profilesRequest) MergeSplit(_ context.Context, maxSize int, szt expor req.pd.ResourceProfiles().MoveAndAppendTo(dst.pd.ResourceProfiles()) }*/ -func (req *profilesRequest) split(maxSize int, sz sizer.ProfilesSizer) []exporterhelper.Request { +func (req *profilesRequest) split(maxSize int, sz sizer.ProfilesSizer) ([]exporterhelper.Request, error) { var res []exporterhelper.Request for req.size(sz) > maxSize { pd, rmSize := extractProfiles(req.pd, maxSize, sz) + if pd.SampleCount() == 0 { + return res, fmt.Errorf("one sample size is greater than max size, dropping items: %d", req.pd.SampleCount()) + } req.setCachedSize(req.size(sz) - rmSize) res = append(res, newProfilesRequest(pd)) } + res = append(res, req) - return res + return res, nil } // extractProfiles extracts a new profiles with a maximum number of samples. diff --git a/exporter/exporterhelper/xexporterhelper/profiles_batch_test.go b/exporter/exporterhelper/xexporterhelper/profiles_batch_test.go index 4f45793b05a9..877d1370f642 100644 --- a/exporter/exporterhelper/xexporterhelper/profiles_batch_test.go +++ b/exporter/exporterhelper/xexporterhelper/profiles_batch_test.go @@ -30,7 +30,7 @@ func TestMergeProfiles(t *testing.T) { func TestMergeProfilesInvalidInput(t *testing.T) { pr2 := newProfilesRequest(testdata.GenerateProfiles(3)) _, err := pr2.MergeSplit(context.Background(), 0, exporterhelper.RequestSizerTypeItems, &requesttest.FakeRequest{Items: 1}) - assert.Error(t, err) + require.Error(t, err) } func TestMergeSplitProfiles(t *testing.T) {