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