diff --git a/README.md b/README.md index 5648463..fa07152 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,8 @@ For more details, see [documention](./docs) ## Roadmap - [x] TableBuilder - [x] TableReader -- [ ] TableCache +- [x] TableCache +- [ ] BlockCache - [x] LogWriter - [x] LogReader - [x] WriteBatch diff --git a/package.json b/package.json index 7a0f56f..dd4a5bf 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,6 @@ "eslint-config-prettier": "^6.4.0", "eslint-plugin-prettier": "^3.1.1", "jest": "^24.9.0", - "lru-cache": "^5.1.1", "rimraf": "^3.0.0", "typescript": "~3.4" }, @@ -42,6 +41,7 @@ "assert": "^2.0.0", "buffer": "^5.4.3", "buffer-crc32": "^0.2.13", + "lru-cache": "^5.1.1", "varint": "^5.0.0" }, "jest": { diff --git a/src/SSTableCache.ts b/src/SSTableCache.ts index 48a4b22..51c76ff 100644 --- a/src/SSTableCache.ts +++ b/src/SSTableCache.ts @@ -13,6 +13,7 @@ import { Options, ReadOptions } from './Options' import Slice from './Slice' import { Entry } from './Format' import IteratorHelper from './IteratorHelper' +import LRUCache from 'lru-cache' // TODO use LRUCache @@ -27,11 +28,20 @@ export class TableCache { this._env = options.env this._dbpath = dbpath this._options = options + this._cache = new LRUCache({ + max: entries, + async dispose(key: number, tf: TableAndFile): Promise { + try { + await tf.file.close() + } catch (e) {} + }, + }) } _env: Env _dbpath: string _options: Options + _cache: LRUCache public async get( options: ReadOptions, @@ -57,19 +67,26 @@ export class TableCache { } async findTable(fileNumber: number, fileSize: number): Promise { - const tableFilename = getTableFilename(this._dbpath, fileNumber) - let status = new Status(this._env.open(tableFilename, 'r+')) - const tf = {} as TableAndFile - if (await status.ok()) { - tf.file = (await status.promise) as FileHandle - status = new Status(Table.open(this._options, tf.file)) - } - if (await status.ok()) { - tf.table = (await status.promise) as Table - status = new Status(Promise.resolve(tf)) + let status = new Status() + const cachedTf = this._cache.get(fileNumber) + if (!cachedTf) { + const tableFilename = getTableFilename(this._dbpath, fileNumber) + status = new Status(this._env.open(tableFilename, 'r+')) + const tf = {} as TableAndFile + if (await status.ok()) { + tf.file = (await status.promise) as FileHandle + status = new Status(Table.open(this._options, tf.file)) + } + if (await status.ok()) { + tf.table = (await status.promise) as Table + this._cache.set(fileNumber, tf) + status = new Status(Promise.resolve(tf)) + } else { + // We do not cache error results so that if the error is transient, + // or somebody repairs the file, we recover automatically. + } } else { - // We do not cache error results so that if the error is transient, - // or somebody repairs the file, we recover automatically. + status = new Status(Promise.resolve(cachedTf)) } return status