Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added retry mechanism for close contentReader in download. #1099

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from

Conversation

agrasth
Copy link

@agrasth agrasth commented Mar 18, 2025

The changes introduce a refined retry mechanism for simulating and recovering from forced errors encountered during the cleanup of temporary files in the ContentReader's Close method.

Details:
Added Retry Mechanism with Forced Error Simulation:

  • Introduced retryOperation function to encapsulate retry logic with a forced error simulation feature.
  • The retryOperation function takes the number of attempts, a simulation flag, and the operation to be retried as parameters.
  • The retry logic forces errors for the first maxForcedAttempts if the simulateError flag is set to true.
    Controlled Forced Error Simulation:
  • Introduced global variables downloadAttempts and maxForcedAttempts to manage the number of simulated download attempts.

  • All tests passed. If this feature is not already covered by the tests, I added new tests.
  • All static analysis checks passed.
  • This pull request is on the dev branch.
  • I used gofmt for formatting the code before submitting the pull request.

func (cr *ContentReader) Close() error {
for _, filePath := range cr.filesPaths {
if filePath == "" {
continue
}
if err := errorutils.CheckError(os.Remove(filePath)); err != nil {
// Check if file exists before attempting to remove
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Check if file exists before attempting to remove
func (cr *ContentReader) Close() error {
for _, filePath := range cr.filesPaths {
if filePath == "" {
continue
}
if err := removeFileWithRetry(filePath); err != nil {
return fmt.Errorf("failed to close reader: %w", err)
}
}
cr.filesPaths = nil
return nil
}
func removeFileWithRetry(filePath string) error {
// Check if file exists before attempting to remove
if _, err := os.Stat(filePath); os.IsNotExist(err) {
log.Debug("File does not exist: %s", filePath)
return nil
}
log.Debug("Attempting to remove file: %s", filePath)
executor := retryexecutor.RetryExecutor{
Context: context.Background(),
MaxRetries: 5,
RetriesIntervalMilliSecs: 100,
ErrorMessage: "Failed to remove file",
LogMsgPrefix: "Attempting removal",
ExecutionHandler: func() (bool, error) {
return false, errorutils.CheckError(os.Remove(filePath))
},
}
return executor.Execute()
}

func (cr *ContentReader) Close() error {
simulateError := false // Set to true in testing, and false in production

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this simulate error?
we can simply go ahead and retry

@@ -93,13 +98,43 @@ func (cr *ContentReader) Reset() {
cr.once = new(sync.Once)
}

// Cleanup the reader data.
// Retry function with forced error simulation

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, we should create a general utility function for retry and pass a function along with comparables for the retry operation

// Simulate an error condition for the first few attempts if `simulateError` is true and attempts are less than maxForcedAttempts
if simulateError && downloadAttempts < maxForcedAttempts {
lastErr = fmt.Errorf("forced error simulation at attempt %d", downloadAttempts+1)
downloadAttempts++

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we referring download here, aren't you just trying to close a opened file?

log.Info("Operation succeeded at attempt %d", i+1)
return nil
}
log.Warn("Retry attempt %d failed: %v", i+1, lastErr)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log.Warn("Retry attempt %d failed: %v", i+1, lastErr)
log.Warn("Retry attempt %d failed: %v", i+1, lastErr.Error())

return nil
}
log.Warn("Retry attempt %d failed: %v", i+1, lastErr)
time.Sleep(time.Millisecond * 100) // Adding a small delay between retries

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is sleep required?

@fluxxBot
Copy link

I was able to see the same issue here, please check if this could be of any help.
cc: @agrasth @bhanurp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants