UNPKG

wabac

Version:

A versioned cache backed by cloud storage

98 lines (81 loc) 2.27 kB
'use strict' const crypto = require('crypto') const { promisify } = require('util') const gzip = promisify(require('zlib').gzip) const { isErrorReason } = require('./api-errors') class Cache { constructor({ storage, bucketName, defaultContentType }) { Object.assign(this, { storage, bucketName, defaultContentType }) } async initialize({ location }) { await this.storage.createBucket(this.bucketName, { location, storageClass: 'regional', versioning: { enabled: true }, }) } async initializeIfNeeded(options) { try { await this.initialize(options) } catch (e) { if (!isErrorReason(e, 'conflict')) { throw e } } } get bucket() { return this.storage.bucket(this.bucketName) } async get(key, { maxAgeSeconds }) { throw Error('Not implemented') } async getVersion(key, asOfDate) { throw Error('Not implemented') } async delete(key) { throw Error('Not implemented') } async put(key, value, options = {}) { const { contentType = this.defaultContentType } = options const commonOpts = { gzip: true, // Disable resumable uploads, because they leak state across // processes, leading to unpredictable behavior. // https://github.com/googleapis/nodejs-storage/issues/217 resumable: false, } // Create a new key. try { await this.bucket.file(key, { generation: 0 }).save(value, { contentType, ...commonOpts, }) return } catch (e) { if (!isErrorReason(e, 'conditionNotMet')) { throw e } } // Update an existing key. const existing = this.bucket.file(key) const [{ md5Hash: oldHash }] = await existing.getMetadata() const newHash = crypto .createHash('md5') .update(await gzip(value)) .digest('base64') if (oldHash === newHash) { // Setting the metadata has a side effect of updating the // `metageneration` and the `updated` date. await existing.setMetadata({}) } else { await existing.save(value, { ...commonOpts }) } } async drop(key) { throw Error('Not implemented') } async dropAll() { throw Error('Not implemented') } } module.exports = Cache