Skip to content

Commit dfceeb7

Browse files
authored
[libbeat] Fix write error in ensureWriter.Write (elastic#20112) (elastic#20145)
(cherry picked from commit 11c0a37)
1 parent 46df8af commit dfceeb7

File tree

2 files changed

+82
-1
lines changed

2 files changed

+82
-1
lines changed

libbeat/statestore/backend/memlog/util.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func (e *ensureWriter) Write(p []byte) (int, error) {
5555
for len(p) > 0 {
5656
n, err := e.w.Write(p)
5757
N, p = N+n, p[n:]
58-
if isRetryErr(err) {
58+
if err != nil && !isRetryErr(err) {
5959
return N, err
6060
}
6161
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package memlog
19+
20+
import (
21+
"syscall"
22+
"testing"
23+
)
24+
25+
// A mock Writer implementation that always returns a configurable
26+
// error on the first write call, to test error handling in ensureWriter.
27+
type mockErrorWriter struct {
28+
errorType error
29+
reportedError bool
30+
}
31+
32+
func (mew *mockErrorWriter) Write(data []byte) (n int, err error) {
33+
if !mew.reportedError {
34+
mew.reportedError = true
35+
return 0, mew.errorType
36+
}
37+
return len(data), nil
38+
}
39+
40+
func TestEnsureWriter_RetriableError(t *testing.T) {
41+
// EAGAIN is retriable, ensureWriter.Write should succeed.
42+
errorWriter := &mockErrorWriter{errorType: syscall.EAGAIN}
43+
bytes := []byte{1, 2, 3}
44+
writer := &ensureWriter{errorWriter}
45+
written, err := writer.Write(bytes)
46+
if err != nil {
47+
t.Fatalf("ensureWriter shouldn't propagate retriable errors")
48+
}
49+
if written != len(bytes) {
50+
t.Fatalf("Expected %d bytes written, got %d", len(bytes), written)
51+
}
52+
}
53+
54+
func TestEnsureWriter_NonRetriableError(t *testing.T) {
55+
// EINVAL is not retriable, ensureWriter.Write should return an error.
56+
errorWriter := &mockErrorWriter{errorType: syscall.EINVAL}
57+
bytes := []byte{1, 2, 3}
58+
writer := &ensureWriter{errorWriter}
59+
written, err := writer.Write(bytes)
60+
if err != syscall.EINVAL {
61+
t.Fatalf("ensureWriter should propagate nonretriable errors")
62+
}
63+
if written != 0 {
64+
t.Fatalf("Expected 0 bytes written, got %d", written)
65+
}
66+
}
67+
68+
func TestEnsureWriter_NoError(t *testing.T) {
69+
// This tests the case where the underlying writer returns with no error,
70+
// but without writing the full buffer.
71+
var bytes []byte = []byte{1, 2, 3}
72+
errorWriter := &mockErrorWriter{errorType: nil}
73+
writer := &ensureWriter{errorWriter}
74+
written, err := writer.Write(bytes)
75+
if err != nil {
76+
t.Fatalf("ensureWriter should only error if the underlying writer does")
77+
}
78+
if written != len(bytes) {
79+
t.Fatalf("Expected %d bytes written, got %d", len(bytes), written)
80+
}
81+
}

0 commit comments

Comments
 (0)