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

fix: Simplified IFileSystem #3641

Merged
merged 5 commits into from
Oct 1, 2024
Merged

fix: Simplified IFileSystem #3641

merged 5 commits into from
Oct 1, 2024

Conversation

bitsandfoxes
Copy link
Contributor

@bitsandfoxes bitsandfoxes commented Sep 30, 2024

TLDR;

The responsibility to check whether a system is allowed to write to disk or not falls onto the system itself. The ReadOnlyFileSystem is only there to prevent regressions and crashes on platforms like the Switch.

Any attempts to write to disk and fail can now simply logged as Error because that's what they are.

Context

The idea was to have IFileSystem be the abstraction layer and be swapped between ReadOnly and ReadWrite and have it be clever enough to log whether the operation failed or was disallowed.

Having it smart enough is not really feasable. The caller is still interested whether it failed or was disallowed for logging purposes. Having the caller simply attempt to write to disk and then not care whether it was allowed to do so is not possible.

So we end up checking the success/failure/disallowed with every call. That is noisy. So we're checking whether we're allowed to write to disk before even attempting. This making the FileOperationResult pointless. We can just log and skip when we know we can't write to disk in the first place.

Comment on lines -20 to -37

internal static class JsonSerializableExtensions
{
public static void WriteToFile(this ISentryJsonSerializable serializable, IFileSystem fileSystem, string filePath, IDiagnosticLogger? logger)
{
var result = fileSystem.CreateFileForWriting(filePath, out var file);
if (result is not FileOperationResult.Success)
{
return;
}

using var writer = new Utf8JsonWriter(file);

serializable.WriteTo(writer, logger);
writer.Flush();
file.Dispose();
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is only used once in the GlobalSessionsManager. Success/Failure is getting handled there and does not need to be abstracted away into an extension.

@bruno-garcia
Copy link
Member

Copy link
Member

@bruno-garcia bruno-garcia left a comment

Choose a reason for hiding this comment

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

Having integration tests related to e2e and with file I/O (sessions etc) have two runs, one with this option set to true and other to false, would really help us avoid regression

src/Sentry/GlobalSessionManager.cs Outdated Show resolved Hide resolved
src/Sentry/Internal/Http/CachingTransport.cs Outdated Show resolved Hide resolved
internal interface IFileSystem
{
// Note: You are responsible for handling success/failure when attempting to write to disk.
// You are required to check for `Options.FileWriteDisabled` whether you are allowed to call any writing operations.
Copy link
Member

Choose a reason for hiding this comment

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

This is a good call out. Adding a sexton like or (can't recall the options) above methods in the interface with a snippet might make it even more obvious

@@ -2,20 +2,24 @@ namespace Sentry.Internal;

internal class ReadOnlyFileSystem : FileSystemBase
{
public override FileOperationResult CreateDirectory(string path) => FileOperationResult.Disabled;
// Note: You are responsible for handling success/failure when attempting to write to disk.
Copy link
Member

Choose a reason for hiding this comment

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

this won't show up in the IDE at call site. Needs

Copy link
Collaborator

Choose a reason for hiding this comment

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

These interfaces and classes are all internal, so it looks like the only place to document this is in some remarks or something on SentryOptions.DisableFileWrite.

@jamescrosswell
Copy link
Collaborator

Having the caller simply attempt to write to disk and then not care whether it was allowed to do so is not possible.

@bitsandfoxes can you provide a bit more context around when/why that's not possible?

@bitsandfoxes
Copy link
Contributor Author

@bitsandfoxes can you provide a bit more context around when/why that's not possible?

Because the caller typically would like to log whether the failed write attempt failed because of a failure or because writing has been disabled. So I ended up with constructs like this

var result = _options.FileSystem.CreateDirectory(_persistenceDirectoryPath);
if (result is not FileOperationResult.Success)
{
    if (result is not FileOperationResult.Disabled)
    {
        _options.LogDebug("Disabled.");
    }
    else
    {
        _options.LogError("Failed.");    
    }
    
    return;
}

all over the place. So I could not escape checking for Options.FileWriteDisabled, either directly or indirectly anyway. I think it's simpler to be explicit about it.

@bitsandfoxes bitsandfoxes merged commit 7da3cf3 into main Oct 1, 2024
22 checks passed
@bitsandfoxes bitsandfoxes deleted the fix/filesystem branch October 1, 2024 23:32
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