Skip to content

Commit

Permalink
feat(FileSystemVault): Add file change debouncing mechanism
Browse files Browse the repository at this point in the history
This commit adds a file change debouncing mechanism to the `FileSystemVault` class. It introduces a dictionary of file change locks, which helps mitigate duplicate dispatches of a given file change event. The debounce period between file changes is set to 100 milliseconds.

The code changes include:
- Addition of `_fileChangeLocks` dictionary to store file change locks
- Introduction of `DebouncePeriod` constant for setting the debounce period
- Implementation of logic to check and update the reference hash at the time of lock acquisition
- Dispatching the `VaultUpdate` event only if the debounce period has elapsed since the last change
  • Loading branch information
SakuraIsayeki committed Jul 5, 2024
1 parent ab8fb33 commit 0fb0f76
Showing 1 changed file with 37 additions and 2 deletions.
39 changes: 37 additions & 2 deletions Vaults/Nodsoft.MoltenObsidian.Vaults.FileSystem/FileSystemVault.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,18 @@ public sealed class FileSystemVault : IWritableVault

/// <inheritdoc />
public event IVault.VaultUpdateEventHandler? VaultUpdate;


/// <summary>
/// Provides a dictionary of file change locks, by last datetime modified.
/// This is used to mitigate duplicate dispatches of a given file change event.
/// </summary>
private readonly ConcurrentDictionary<string, (SemaphoreSlim, DateTime)> _fileChangeLocks = [];

/// <summary>
/// Debounce period between file changes.
/// </summary>
private static readonly TimeSpan DebouncePeriod = TimeSpan.FromMilliseconds(100);

/// <summary>
/// Gets the default list of folders to ignore when loading a vault.
/// </summary>
Expand Down Expand Up @@ -228,7 +238,32 @@ private async ValueTask OnItemChangedAsync(object sender, FileSystemEventArgs e)
{
if (_files.TryGetValue(relativePath, out IVaultFile? file))
{
await (VaultUpdate?.Invoke(this, new(UpdateType.Update, file)) ?? new());
// Ensure hashes differ
(SemaphoreSlim semaphore, _) = _fileChangeLocks.GetOrAdd(relativePath, _ => (new(1, 1), DateTime.UnixEpoch));

if (semaphore.CurrentCount is 0)
{
return;
}

await semaphore.WaitAsync();

try
{
// Update w/ reference hash at time of lock
(_, DateTime lastChange) = _fileChangeLocks[relativePath];
DateTime current = DateTime.UtcNow;

if (lastChange.Add(DebouncePeriod) < current)
{
await (VaultUpdate?.Invoke(this, new(UpdateType.Update, file)) ?? new());
_fileChangeLocks[relativePath] = new(semaphore, current);
}
}
finally
{
semaphore.Release();
}
}
}
}
Expand Down

0 comments on commit 0fb0f76

Please sign in to comment.