Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions docs/src/pages/en/usage/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,9 @@ These actions are mainly for troubleshooting. Use them carefully.

Sync Engine keeps records of which local and remote files were last matched. It uses these records to tell a new change from a conflict and to plan safe sync operations.

- **Clear vault records** removes records for the current backend + vault combo.
- **Clear all records** removes records for all vaults on this machine and installed modules.
Clicking **Clear** button removes records for the current backend + vault combo.

These actions do not directly delete files, but the next sync will have less history and may plan uploads, downloads, or deletions differently. The setting warns that clearing records can cause data loss. Export or back up important files before using it.
These actions do not directly delete files, but the next sync will have less history and may plan uploads, downloads, or deletions differently. The setting warns that clearing records can cause data loss. Only use it when you manually modified remote files and the plugin already reports wrong operations.

### Export Logs to File

Expand Down
10 changes: 9 additions & 1 deletion packages/plugin/src/fs/wrappers/asymmetric-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,15 @@ class AsymmetricStorageFs implements WrappedFs {
const stats = await this.original.list(this.flattenKey(key), () => 'include');
const seen = new Set<string>();
const result: Array<Stat> = [];
let ignoredCount = 0;
await Promise.all(
stats.map(async (stat, index) => {
const inflated = this.inflateStat(stat);
if (!inflated) {
ignoredCount++;
return;
}
if (
!inflated ||
!isSub(key, inflated.key) ||
seen.has(inflated.key) ||
(await reporter({
Expand All @@ -175,6 +179,10 @@ class AsymmetricStorageFs implements WrappedFs {
result.push(inflated);
}),
);
if (ignoredCount / (stats.length || 1) >= 0.3)
throw new Error(
"There are too many files at remote that don't adopt asymmetric storage, maybe you want to turn it off in settings.",
);
return result;
}

Expand Down
10 changes: 4 additions & 6 deletions packages/plugin/test/fs-asymmetric-storage-wrapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,11 @@ test('list should infer folder anchors from remoteStatContext and return hierarc
expect(remote.calls.list).toStrictEqual(['/']);
});

test('list should skip malformed or orphan flattened entries without throwing', async () => {
test('list should throw when encountering too many malformed or orphan flattened entries without proceeding', () => {
seedRemoteContext(file('00000abcde~folder'));
const remote = fs({
control: {
list: () => [
folder('/'),
file('bad-key', { size: 1, uid: 'bad' }),
file('zzzzz~lost.md', { size: 2, uid: 'orphan-file' }),
file('zzzzzqqqqq~ghost', { size: 0, uid: 'orphan-folder' }),
Expand All @@ -68,10 +67,9 @@ test('list should skip malformed or orphan flattened entries without throwing',
});
const wrapper = asymmetricStorageWrapper(remote.fs, store);

expect(await wrapper.list('/', () => 'include')).toStrictEqual([
folder('folder/'),
file('folder/child.md', { size: 4, uid: 'child' }),
]);
expect(() => wrapper.list('/', () => 'include')).toThrow(
"There are too many files at remote that don't adopt asymmetric storage, maybe you want to turn it off in settings.",
);
});

test('mkdir should write empty folder marker file and reuse same generated anchor later', async () => {
Expand Down
Loading