import logjson from './logjson.mjs'
import fsPromises from 'fs/promises'
-let database = new logjson.Database()
+let database = new logjson.Database(5)
await database.open('a.logjson')
+
let transaction = database.Transaction()
transaction.set(
transaction.json_to_logjson(
)
)
await transaction.commit()
+
+/*let*/ transaction = database.Transaction()
+await fsPromises.writeFile(
+ 'a.json',
+ Buffer.from(
+ JSON.stringify(
+ await logjson.logjson_to_json(await transaction.get()),
+ null,
+ 2
+ ) + '\n',
+ 'utf-8'
+ )
+)
+
+await database.kick()
+await database.kick()
+await database.kick()
+await database.kick()
+await database.kick()
+
+/*let*/ transaction = database.Transaction()
+await fsPromises.writeFile(
+ 'b.json',
+ Buffer.from(
+ JSON.stringify(
+ await logjson.logjson_to_json(await transaction.get()),
+ null,
+ 2
+ ) + '\n',
+ 'utf-8'
+ )
+)
+transaction.rollback()
+
await database.close()
import fsPromises from 'fs/promises'
import Mutex from './Mutex.mjs'
+class CachedValue {
+ constructor(value, stale_count) {
+ this.value = value
+ this.stale_count = stale_count
+ }
+}
+
let open_angle = Buffer.from('\n<', 'utf-8')
let close_angle = Buffer.from('>\n', 'utf-8')
class Database {
- constructor() {
+ constructor(read_timeout) {
+ this.read_timeout = read_timeout || 3600
this.mutex = new Mutex()
this.log = null
this.eof = 0
this.value = undefined
+ this.read_cache = {}
}
async open(path) {
await this.mutex.acquire()
assert(this.log === null)
+ this.eof = 0
+ this.value = undefined
+ this.read_cache = {}
+
try {
this.log = await fsPromises.open(path, 'r+')
}
throw error
console.log('warning: can\'t find database file, creating')
-
this.log = await fsPromises.open(path, 'w+')
- this.eof = 0
- this.value = undefined
this.mutex.release()
return
}
catch (error) {
console.log('warning: can\'t find root, truncating database file')
-
await this.log.truncate(0)
- this.eof = 0
- this.value = undefined
this.mutex.release()
return
// optional: trim any garbage off the end of the database file
if (eof < this.eof) {
console.log('warning: garbage after root, truncating database file')
-
await this.log.truncate(eof)
this.eof = eof
}
async read(ptr_len) {
let [ptr, len] = ptr_len
+ if (Object.prototype.hasOwnProperty.call(this.read_cache, ptr)) {
+ //console.log('hit', ptr)
+ let cached_value = this.read_cache[ptr]
+ cached_value.stale_count = this.read_timeout
+ return cached_value.value
+ }
+
let buffer = Buffer.alloc(len)
assert(
(await this.log.read(buffer, 0, len, ptr)).bytesRead === len
)
- return JSON.parse(buffer.toString('utf-8'))
+ let value = JSON.parse(buffer.toString('utf-8'))
+
+ if (this.read_timeout) //{
+ //console.log('add', ptr)
+ this.read_cache[ptr] = new CachedValue(value, this.read_timeout)
+ //}
+ return value
}
async write(value) {
let ptr = this.eof
let len = buffer.length
+
+ if (this.read_timeout) {
+ //console.log('addw', ptr)
+ assert(!Object.prototype.hasOwnProperty.call(this.read_cache, ptr))
+ this.read_cache[ptr] = new CachedValue(value, this.read_timeout)
+ }
+
assert(
(await this.log.write(buffer, 0, len, ptr)).bytesWritten === len
)
this.mutex.acquire()
return new Transaction(this, this.value)
}
+
+ async kick() {
+ for (let i in this.read_cache) {
+ let cached_value = this.read_cache[i]
+ if (--cached_value.stale_count === 0) //{
+ //console.log('stale', i)
+ delete this.read_cache[i]
+ //}
+ }
+ }
}
class Transaction {