Hopefully finally squashed the race condition that would randomly reload files

pull/570/head
Zef Hemel 2023-11-20 17:08:29 +01:00
parent d388796b1c
commit 79611a27e0
2 changed files with 104 additions and 68 deletions

View File

@ -12,7 +12,9 @@ import type { SpacePrimitives } from "./space_primitives.ts";
* - page:deleted (string)
*/
export class EventedSpacePrimitives implements SpacePrimitives {
// Avoid file listing while performing another fetch (read, write, meta) operation
alreadyFetching = false;
initialFileListLoad = true;
spaceSnapshot: Record<string, number> = {};
@ -28,14 +30,19 @@ export class EventedSpacePrimitives implements SpacePrimitives {
async fetchFileList(): Promise<FileMeta[]> {
const newFileList = await this.wrapped.fetchFileList();
if (this.alreadyFetching) {
// Avoid race conditions
// Avoid race conditions, let's just skip this in terms of event triggering: that's ok
return newFileList;
}
try {
this.alreadyFetching = true;
const deletedFiles = new Set<string>(Object.keys(this.spaceSnapshot));
for (const meta of newFileList) {
const oldHash = this.spaceSnapshot[meta.name];
const newHash = meta.lastModified;
// Update in snapshot
this.spaceSnapshot[meta.name] = newHash;
// Check what happened to the file
if (
(
// New file scenario
@ -46,13 +53,16 @@ export class EventedSpacePrimitives implements SpacePrimitives {
oldHash !== newHash
)
) {
await this.dispatchEvent("file:changed", meta.name);
await this.dispatchEvent(
"file:changed",
meta.name,
false,
oldHash,
newHash,
);
}
// Page found, not deleted
deletedFiles.delete(meta.name);
// Update in snapshot
this.spaceSnapshot[meta.name] = newHash;
}
for (const deletedFile of deletedFiles) {
@ -66,17 +76,27 @@ export class EventedSpacePrimitives implements SpacePrimitives {
}
await this.dispatchEvent("file:listed", newFileList);
this.alreadyFetching = false;
this.initialFileListLoad = false;
return newFileList;
} finally {
this.alreadyFetching = false;
}
}
async readFile(
name: string,
): Promise<{ data: Uint8Array; meta: FileMeta }> {
try {
// Fetching mutex
this.alreadyFetching = true;
// Fetch file
const data = await this.wrapped.readFile(name);
this.triggerEventsAndCache(name, data.meta.lastModified);
return data;
} finally {
this.alreadyFetching = false;
}
}
async writeFile(
@ -85,6 +105,8 @@ export class EventedSpacePrimitives implements SpacePrimitives {
selfUpdate?: boolean,
meta?: FileMeta,
): Promise<FileMeta> {
try {
this.alreadyFetching = true;
const newMeta = await this.wrapped.writeFile(
name,
data,
@ -110,6 +132,9 @@ export class EventedSpacePrimitives implements SpacePrimitives {
});
}
return newMeta;
} finally {
this.alreadyFetching = false;
}
}
triggerEventsAndCache(name: string, newHash: number) {
@ -124,6 +149,7 @@ export class EventedSpacePrimitives implements SpacePrimitives {
async getFileMeta(name: string): Promise<FileMeta> {
try {
this.alreadyFetching = true;
const newMeta = await this.wrapped.getFileMeta(name);
this.triggerEventsAndCache(name, newMeta.lastModified);
return newMeta;
@ -137,10 +163,14 @@ export class EventedSpacePrimitives implements SpacePrimitives {
}
}
throw e;
} finally {
this.alreadyFetching = false;
}
}
async deleteFile(name: string): Promise<void> {
try {
this.alreadyFetching = true;
if (name.endsWith(".md")) {
const pageName = name.substring(0, name.length - 3);
await this.dispatchEvent("page:deleted", pageName);
@ -149,5 +179,8 @@ export class EventedSpacePrimitives implements SpacePrimitives {
await this.wrapped.deleteFile(name);
delete this.spaceSnapshot[name];
await this.dispatchEvent("file:deleted", name);
} finally {
this.alreadyFetching = false;
}
}
}

View File

@ -53,11 +53,14 @@ export class PlugSpacePrimitives implements SpacePrimitives {
for (const pm of await plug.invoke(name, [])) {
allFiles.push(pm);
}
} catch (e) {
} catch (e: any) {
if (!e.message.includes("not available")) {
// Don't report "not available in" environments errors
console.error("Error listing files", e);
}
}
}
}
const files = await this.wrapped.fetchFileList();
for (const pm of files) {
allFiles.push(pm);