import { stat } from "fs" ; import Loki from "lokijs" ; import { rimrafAsync } from "../utils/utils" ; import AllExtentsAsyncIterator from "./AllExtentsAsyncIterator" ; import IExtentMetadataStore , { IExtentModel } from "./IExtentMetadataStore" ; export class implements IExtentMetadata export default class LokiExtentMetadata implements IExtentMetadataStore { private readonly db : Loki ; private initialized : boolean = false ; private closed : boolean = true ; private readonly EXTENTS_COLLECTION = "$EXTENTS_COLLECTION$" ; public constructor ( public readonly lokiDBPath ) { this . db = new Loki ( lokiDBPath , { autosave : true , autosaveInterval : 5000 } ) ; } public isInitialized ( ) { return this . initialized ; } public isClosed ( ) { return this . closed ; } public async init ( ) { await new Promise < void > ( ( resolve , reject ) => { stat ( this . lokiDBPath , ( statError , stats ) => { if ( ! statError ) { this . db . loadDatabase ( { } , ( dbError ) => { if ( dbError ) { reject ( dbError ) ; } else { resolve ( ) ; } } ) ; } else { resolve ( ) ; } } ) ; } ) ; if ( this . db . getCollection ( this . EXTENTS_COLLECTION ) === null ) { this . db . addCollection ( this . EXTENTS_COLLECTION , { indices : [ "id" ] } ) ; } await new Promise < void > ( ( resolve , reject ) => { this . db . saveDatabase ( ( err ) => { if ( err ) { reject ( err ) ; } else { resolve ( ) ; } } ) ; } ) ; this . initialized = true ; this . closed = false ; } returns { Promise < void > } memberof public async close ( ) { await new Promise < void > ( ( resolve , reject ) => { this . db . close ( ( err ) => { if ( err ) { reject ( err ) ; } else { resolve ( ) ; } } ) ; } ) ; this . closed = true ; } returns { Promise < void > } memberof public async clean ( ) { if ( this . isClosed ( ) ) { await rimrafAsync ( this . lokiDBPath ) ; return ; } throw new Error ( `Cannot clean LokiExtentMetadata, it's not closed.` ) ; } param { IExtentModel } extent returns { Promise < void > } memberof public async updateExtent ( extent ) { const coll = this . db . getCollection ( this . EXTENTS_COLLECTION ) ; const doc = coll . findOne ( { id : extent . id } ) ; if ( ! doc ) { coll . insert ( extent ) ; return ; } doc . size = extent . size ; doc . LastModifyInMS = extent . lastModifiedInMS ; coll . update ( doc ) ; } param { string } id param { number } maxResults param { number } marker param { Date } queryTime param { number } protectTimeInMs returns { ( Promise < [ IExtentModel [ ] , number | undefined ] > ) } memberof public async listExtents ( id ? , maxResults ? , marker ? , queryTime ? , protectTimeInMs ? ) { const query = { } ; if ( id !== undefined ) { query . id = id ; } if ( maxResults === undefined ) { maxResults = 5000 ; } if ( protectTimeInMs === undefined ) { protectTimeInMs = 0 ; } if ( queryTime !== undefined ) { query . LastModifyInMS = { $lt : queryTime . getTime ( ) - protectTimeInMs } ; } query . $loki = { $gt : marker } ; const coll = this . db . getCollection ( this . EXTENTS_COLLECTION ) ; const docs = coll . chain ( ) . find ( query ) . limit ( maxResults ) . data ( ) ; if ( docs . length < maxResults ) { return [ docs , undefined ] ; } else { const nextMarker = docs [ docs . length - 1 ] . $loki ; return [ docs , nextMarker ] ; } } param { string } extentId returns { Promise < void > } memberof public async deleteExtent ( extentId ) { const coll = this . db . getCollection ( this . EXTENTS_COLLECTION ) ; return coll . findAndRemove ( { id : extentId } ) ; } public iteratorExtents ( ) { return new AllExtentsAsyncIterator ( this ) ; } param { string } extentId returns { Promise < string > } memberof public async getExtentLocationId ( extentId ) { const coll = this . db . getCollection ( this . EXTENTS_COLLECTION ) ; const doc = coll . findOne ( { id : extentId } ) as IExtentModel ; return doc . locationId ; } }