Skip to content

Commit d31ed5b

Browse files
authored
[Upgrade Details] For critical state transitions, fsync upgrade marker file (#3836)
* Fix bug where markerFilePath was not being used * Fsync marker file write for critical upgrade transitions * Add unit test
1 parent 272c8e4 commit d31ed5b

File tree

7 files changed

+71
-10
lines changed

7 files changed

+71
-10
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
package upgrade
6+
7+
import (
8+
"fmt"
9+
"os"
10+
)
11+
12+
func writeMarkerFileCommon(markerFile string, markerBytes []byte, shouldFsync bool) error {
13+
f, err := os.OpenFile(markerFile, os.O_WRONLY|os.O_CREATE, 0600)
14+
if err != nil {
15+
return fmt.Errorf("failed to open upgrade marker file for writing: %w", err)
16+
}
17+
defer f.Close()
18+
19+
if _, err := f.Write(markerBytes); err != nil {
20+
return fmt.Errorf("failed to write upgrade marker file: %w", err)
21+
}
22+
23+
if !shouldFsync {
24+
return nil
25+
}
26+
27+
if err := f.Sync(); err != nil {
28+
return fmt.Errorf("failed to sync upgrade marker file to disk: %w", err)
29+
}
30+
31+
return nil
32+
}

internal/pkg/agent/application/upgrade/marker_access_other.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ func readMarkerFile(markerFile string) ([]byte, error) {
2525

2626
// On non-Windows platforms, writeMarkerFile simply writes the marker file.
2727
// See marker_access_windows.go for behavior on Windows platforms.
28-
func writeMarkerFile(markerFile string, markerBytes []byte) error {
29-
return os.WriteFile(markerFilePath(), markerBytes, 0600)
28+
func writeMarkerFile(markerFile string, markerBytes []byte, shouldFsync bool) error {
29+
return writeMarkerFileCommon(markerFile, markerBytes, shouldFsync)
3030
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
package upgrade
6+
7+
import (
8+
"os"
9+
"path/filepath"
10+
"testing"
11+
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestWriteMarkerFile(t *testing.T) {
16+
tmpDir := t.TempDir()
17+
markerFile := filepath.Join(tmpDir, markerFilename)
18+
19+
markerBytes := []byte("foo bar")
20+
err := writeMarkerFile(markerFile, markerBytes, true)
21+
require.NoError(t, err)
22+
23+
data, err := os.ReadFile(markerFile)
24+
require.NoError(t, err)
25+
require.Equal(t, markerBytes, data)
26+
}

internal/pkg/agent/application/upgrade/marker_access_windows.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ func readMarkerFile(markerFile string) ([]byte, error) {
4949
// mechanism is necessary since the marker file could be accessed by multiple
5050
// processes (the Upgrade Watcher and the main Agent process) at the same time,
5151
// which could fail on Windows.
52-
func writeMarkerFile(markerFile string, markerBytes []byte) error {
52+
func writeMarkerFile(markerFile string, markerBytes []byte, shouldFsync bool) error {
5353
writeFn := func() error {
54-
return os.WriteFile(markerFile, markerBytes, 0600)
54+
return writeMarkerFileCommon(markerFile, markerBytes, shouldFsync)
5555
}
5656

5757
if err := accessMarkerFileWithRetries(writeFn); err != nil {

internal/pkg/agent/application/upgrade/step_mark.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,10 @@ func loadMarker(markerFile string) (*UpdateMarker, error) {
194194
}, nil
195195
}
196196

197-
func SaveMarker(marker *UpdateMarker) error {
197+
// SaveMarker serializes and persists the given upgrade marker to disk.
198+
// For critical upgrade transitions, pass shouldFsync as true so the marker
199+
// file is immediately flushed to persistent storage.
200+
func SaveMarker(marker *UpdateMarker, shouldFsync bool) error {
198201
makerSerializer := &updateMarkerSerializer{
199202
Hash: marker.Hash,
200203
UpdatedOn: marker.UpdatedOn,
@@ -209,7 +212,7 @@ func SaveMarker(marker *UpdateMarker) error {
209212
return err
210213
}
211214

212-
return writeMarkerFile(markerFilePath(), markerBytes)
215+
return writeMarkerFile(markerFilePath(), markerBytes, shouldFsync)
213216
}
214217

215218
func markerFilePath() string {

internal/pkg/agent/application/upgrade/upgrade.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ func (u *Upgrader) Ack(ctx context.Context, acker acker.Acker) error {
253253

254254
marker.Acked = true
255255

256-
return SaveMarker(marker)
256+
return SaveMarker(marker, false)
257257
}
258258

259259
func (u *Upgrader) MarkerWatcher() MarkerWatcher {

internal/pkg/agent/cmd/watch.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func watchCmd(log *logp.Logger, cfg *configuration.Configuration) error {
126126
}
127127

128128
marker.Details.SetState(details.StateRollback)
129-
err = upgrade.SaveMarker(marker)
129+
err = upgrade.SaveMarker(marker, true)
130130
if err != nil {
131131
log.Errorf("unable to save upgrade marker before attempting to rollback: %s", err.Error())
132132
}
@@ -136,7 +136,7 @@ func watchCmd(log *logp.Logger, cfg *configuration.Configuration) error {
136136
log.Error("rollback failed", err)
137137

138138
marker.Details.Fail(err)
139-
err = upgrade.SaveMarker(marker)
139+
err = upgrade.SaveMarker(marker, true)
140140
if err != nil {
141141
log.Errorf("unable to save upgrade marker after rollback failed: %s", err.Error())
142142
}
@@ -146,7 +146,7 @@ func watchCmd(log *logp.Logger, cfg *configuration.Configuration) error {
146146

147147
// watch succeeded - upgrade was successful!
148148
marker.Details.SetState(details.StateCompleted)
149-
err = upgrade.SaveMarker(marker)
149+
err = upgrade.SaveMarker(marker, false)
150150
if err != nil {
151151
log.Errorf("unable to save upgrade marker after successful watch: %s", err.Error())
152152
}

0 commit comments

Comments
 (0)