optimization: reduce number of stat calls
Massimo Melina committed
Aug 25, 2024 at 15:55 UTC
5e3c8e3f20d8d1c663e2063faf08d68a7e4028a9
4 files changed
+9
-7
src/api.get_file_list.ts
+1
-1
@@ -114,7 +114,7 @@ export const get_file_list: ApiHandler = async ({ uri='/', offset, limit, search
114
return name ? { n: name, url, target: node.target } : null
115
const isFolder = await nodeIsDirectory(node)
116
try {
117
- const st = source ? await stat(source) : undefined
117
+ const st = source ? node.stats || await stat(source) : undefined
118
const pl = node.can_list === WHO_NO_ONE ? 'l'
119
: !hasPermission(node, 'can_list', ctx) ? 'L'
120
: ''
src/api.vfs.ts
+2
-2
@@ -32,7 +32,7 @@ const apis: ApiHandlers = {
32
33
async function recur(node=vfs): Promise<VfsNodeAdminSend> {
34
const { source } = node
35
- const stats = !source ? undefined : await stat(source!).catch(() => undefined)
35
+ const stats = !source ? undefined : (node.stats || await stat(source!).catch(() => undefined))
36
const isDir = !nodeIsLink(node) && (!source || (stats?.isDirectory() ?? node.children?.length! > 0))
37
const copyStats: Pick<VfsNodeAdminSend, 'size' | 'ctime' | 'mtime'> = stats ? _.pick(stats, ['size', 'ctime', 'mtime'])
38
: { size: source ? -1 : undefined }
@@ -208,7 +208,7 @@ const apis: ApiHandlers = {
208
if (!files || fileMask && !matching(name))
209
continue
210
try {
211
- const stats = await stat(join(path, name))
211
+ const stats = entry.stats || await stat(join(path, name))
212
list.add({
213
n: name,
214
s: stats.size,
src/vfs.ts
+5
-3
@@ -28,6 +28,7 @@ import { HTTP_FORBIDDEN, HTTP_UNAUTHORIZED, IS_MAC, IS_WINDOWS, MIME_AUTO } from
28
import events from './events'
29
import { expandUsername } from './perm'
30
import { getCurrentUsername } from './auth'
31
+import { Stats } from 'node:fs'
32
33
type Masks = Record<string, VfsNode & { maskOnly?: 'files' | 'folders' }>
34
@@ -50,6 +51,7 @@ export interface VfsNode extends VfsNodeStored { // include fields that are only
51
original?: VfsNode // if this is a temp node but reflecting an existing node
52
parent?: VfsNode // available when original is available
53
isFolder?: boolean
54
+ stats?: Stats
55
}
56
57
export function permsFromParent(parent: VfsNode, child: VfsNode) {
@@ -125,7 +127,7 @@ export async function urlToNode(url: string, ctx?: Koa.Context, parent: VfsNode=
127
return urlToNode(rest, ctx, ret, getRest)
128
if (ret.source)
129
try {
128
- const st = await fs.stat(ret.source) // check existence
130
+ const st = ret.stats || await fs.stat(ret.source) // check existence
131
ret.isFolder = st.isDirectory()
132
}
133
catch {
@@ -196,13 +198,13 @@ export function getNodeName(node: VfsNode) {
198
export async function nodeIsDirectory(node: VfsNode) {
199
if (node.isFolder !== undefined)
200
return node.isFolder
199
- const isFolder = Boolean(node.children?.length || !nodeIsLink(node) && (!node.source || await isDirectory(node.source)))
201
+ const isFolder = Boolean(node.children?.length || !nodeIsLink(node) && (node.stats?.isDirectory() ?? (!node.source || await isDirectory(node.source))))
202
setHidden(node, { isFolder }) // don't make it to the storage (a node.isTemp doesn't need it to be hidden)
203
return isFolder
204
}
205
206
export async function hasDefaultFile(node: VfsNode, ctx: Koa.Context) {
205
- return node.default && await urlToNode(node.default, ctx, node) || undefined
207
+ return node.default && await nodeIsDirectory(node) && await urlToNode(node.default, ctx, node) || undefined
208
}
209
210
export function nodeIsLink(node: VfsNode) {
src/zip.ts
+1
-1
@@ -50,7 +50,7 @@ export async function zipStreamFromFolder(node: VfsNode, ctx: Koa.Context) {
50
if (el.isFolder)
51
return { path: name + '/' }
52
if (!source) return
53
- const st = await fs.stat(source)
53
+ const st = el.stats || await fs.stat(source)
54
if (!st || !st.isFile())
55
return
56
return {