Skip to content

Commit

Permalink
Merge pull request #97 from autonomys/feat/back-hot-cache
Browse files Browse the repository at this point in the history
File download cache
  • Loading branch information
clostao authored Nov 18, 2024
2 parents 869d46a + e80c79c commit 921c328
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 8 deletions.
Binary file modified backend/.yarn/install-state.gz
Binary file not shown.
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@polkadot/types": "^13.0.1",
"@polkadot/util-crypto": "^13.0.2",
"@types/express": "^4.17.21",
"@types/lru-cache": "^7.10.10",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"db-migrate": "^0.11.14",
Expand Down
42 changes: 42 additions & 0 deletions backend/src/services/downloadCache/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { LRUCache } from 'lru-cache'
import { bufferToAsyncIterable } from '../../utils/async.js'

const ONE_GB = 1024 ** 3

const cache = new LRUCache<string, Buffer>({
maxSize: Number(process.env.MAX_CACHE_SIZE ?? ONE_GB),
})

const has = (cid: string) => {
const has = cache.has(cid)
return has
}

const get = (cid: string) => {
const value = cache.get(cid)
if (!value) {
return null
}

return bufferToAsyncIterable(value)
}

const set = async function* (
cid: string,
value: AsyncIterable<Buffer>,
): AsyncIterable<Buffer> {
let buffer = Buffer.alloc(0)
for await (const chunk of value) {
buffer = Buffer.concat([buffer, chunk])
yield chunk
}
cache.set(cid, buffer, {
sizeCalculation: (value) => value.length,
})
}

export const downloadCache = {
has,
get,
set,
}
30 changes: 23 additions & 7 deletions backend/src/useCases/objects/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ import {
} from '../../models/uploads/upload.js'
import { AwaitIterable } from 'interface-store'
import { BlockstoreUseCases } from '../uploads/blockstore.js'
import { asyncIterableToPromiseOfArray } from '../../utils/async.js'
import {
asyncIterableToPromiseOfArray,
bufferToAsyncIterable,
} from '../../utils/async.js'
import { downloadCache } from '../../services/downloadCache/index.js'

const generateFileArtifacts = async (
uploadId: string,
Expand Down Expand Up @@ -215,6 +219,7 @@ const downloadObject = async (
if (!metadata) {
throw new Error(`Metadata with CID ${cid} not found`)
}
const CACHE_THRESHOLD = 150 * 1024 * 1024

const pendingCredits = await UsersUseCases.getPendingCreditsByUserAndType(
reader,
Expand All @@ -224,27 +229,38 @@ const downloadObject = async (
if (pendingCredits < metadata.totalSize) {
throw new Error('Not enough download credits')
}
if (downloadCache.has(cid)) {
return downloadCache.get(cid)!
}

if (metadata.type === 'folder') {
const zip = await retrieveAndReassembleFolderAsZip(
reader,
new PizZip(),
cid,
)
return [
zip.generate({
type: 'nodebuffer',
}),
]
const downloadedBuffer = zip.generate({ type: 'nodebuffer' })
if (metadata.totalSize < CACHE_THRESHOLD) {
downloadCache.set(cid, bufferToAsyncIterable(downloadedBuffer))
}
return bufferToAsyncIterable(downloadedBuffer)
}

const data = retrieveAndReassembleFile(metadata)
if (downloadCache.has(cid)) {
return downloadCache.get(cid)!
}

let data = retrieveAndReassembleFile(metadata)
await UsersUseCases.registerInteraction(
reader,
InteractionType.Download,
metadata.totalSize,
)

if (metadata.totalSize < CACHE_THRESHOLD) {
data = downloadCache.set(cid, data)
}

return data
}

Expand Down
8 changes: 8 additions & 0 deletions backend/src/utils/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,11 @@ export const asyncIterableToPromiseOfArray = async <T>(
}
return result
}

export const bufferToAsyncIterable = (
buffer: Buffer,
): AsyncIterable<Buffer> => {
return (async function* () {
yield buffer
})()
}
17 changes: 17 additions & 0 deletions backend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1942,6 +1942,15 @@ __metadata:
languageName: node
linkType: hard

"@types/lru-cache@npm:^7.10.10":
version: 7.10.10
resolution: "@types/lru-cache@npm:7.10.10"
dependencies:
lru-cache: "npm:*"
checksum: 10c0/ab85558867cb059bebd42074c1cd517eb41efb1db22b9d26dfdc58df01c83ff9c212a562b4ec3d5936418ffb03e626a0f30463026aa5fb5ced41e3b4b4af057f
languageName: node
linkType: hard

"@types/markdown-it@npm:^14.1.1":
version: 14.1.2
resolution: "@types/markdown-it@npm:14.1.2"
Expand Down Expand Up @@ -3012,6 +3021,7 @@ __metadata:
"@types/cors": "npm:^2.8.17"
"@types/express": "npm:^4.17.21"
"@types/jest": "npm:^29.5.12"
"@types/lru-cache": "npm:^7.10.10"
"@types/multer": "npm:^1"
"@types/node": "npm:^22.3.0"
"@types/pg": "npm:^8.11.10"
Expand Down Expand Up @@ -5348,6 +5358,13 @@ __metadata:
languageName: node
linkType: hard

"lru-cache@npm:*":
version: 11.0.2
resolution: "lru-cache@npm:11.0.2"
checksum: 10c0/c993b8e06ead0b24b969c1dbb5b301716aed66e320e9014a80012f5febe280b438f28ff50046b2c55ff404e889351ccb332ff91f8dd175a21f5eae80e3fb155f
languageName: node
linkType: hard

"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0":
version: 10.4.3
resolution: "lru-cache@npm:10.4.3"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Files/ObjectDownloadModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const ObjectDownloadModal = ({
const passwordToUse = password ?? undefined;

try {
fetchFile(metadata.dataCid, passwordToUse);
await fetchFile(metadata.dataCid, passwordToUse);
onClose();
} catch (e) {
if (e instanceof InvalidDecryptKey) {
Expand Down

0 comments on commit 921c328

Please sign in to comment.