1026
src/core/db/block-db.ts
Normal file
1026
src/core/db/block-db.ts
Normal file
File diff suppressed because it is too large
Load Diff
229
src/core/db/db-row.ts
Normal file
229
src/core/db/db-row.ts
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* @project: TERA
|
||||
* @version: Development (beta)
|
||||
* @license: MIT (not for evil)
|
||||
* @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com]
|
||||
* Web: https://terafoundation.org
|
||||
* Twitter: https://twitter.com/terafoundation
|
||||
* Telegram: https://t.me/terafoundation
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
import * as fs from 'fs'
|
||||
|
||||
module.exports = class CDBState extends require("./db")
|
||||
{
|
||||
private FileName;
|
||||
private DataSize;
|
||||
private Format;
|
||||
private WorkStruct;
|
||||
private FileNameFull;
|
||||
private LastHash;
|
||||
private WasUpdate;
|
||||
private BufMap;
|
||||
private BufMapCount;
|
||||
|
||||
constructor(FileName, DataSize, Format, bReadOnly) {
|
||||
super()
|
||||
this.FileName = FileName
|
||||
this.DataSize = DataSize
|
||||
this.Format = Format
|
||||
this.WorkStruct = {}
|
||||
var FI = this.OpenDBFile(this.FileName, !bReadOnly);
|
||||
this.FileNameFull = FI.fname
|
||||
this.LastHash = undefined
|
||||
this.WasUpdate = 1
|
||||
this.BufMap = {}
|
||||
this.BufMapCount = 0
|
||||
setInterval(this.CheckBufMap.bind(this), 1000)
|
||||
}
|
||||
GetMaxNum() {
|
||||
var FI = this.OpenDBFile(this.FileName);
|
||||
var Num = Math.floor(FI.size / this.DataSize) - 1;
|
||||
return Num;
|
||||
}
|
||||
CheckNewNum(Data) {
|
||||
if (Data.Num === undefined)
|
||||
Data.Num = this.GetMaxNum() + 1
|
||||
}
|
||||
Write(Data, RetBuf) {
|
||||
var startTime = process.hrtime();
|
||||
this.LastHash = undefined
|
||||
this.WasUpdate = 1
|
||||
this.CheckNewNum(Data)
|
||||
Data.Num = Math.trunc(Data.Num)
|
||||
this.DeleteMap(Data.Num)
|
||||
var BufWrite = global.BufLib.GetBufferFromObject(Data, this.Format, this.DataSize, this.WorkStruct, 1);
|
||||
var Position = Data.Num * this.DataSize;
|
||||
var FI = this.OpenDBFile(this.FileName, 1);
|
||||
var written = fs.writeSync(FI.fd, BufWrite, 0, BufWrite.length, Position);
|
||||
if (written !== BufWrite.length) {
|
||||
global.TO_ERROR_LOG("DB-ROW", 10, "Error write to file:" + written + " <> " + BufWrite.length)
|
||||
return false;
|
||||
}
|
||||
if (RetBuf) {
|
||||
RetBuf.Buf = BufWrite
|
||||
}
|
||||
if (Position >= FI.size) {
|
||||
FI.size = Position + this.DataSize
|
||||
}
|
||||
global.ADD_TO_STAT_TIME("ROWS_WRITE_MS", startTime)
|
||||
global.ADD_TO_STAT("ROWS_WRITE")
|
||||
return true;
|
||||
}
|
||||
Read(Num, GetBufOnly?) {
|
||||
Num = Math.trunc(Num)
|
||||
var Data;
|
||||
if (isNaN(Num) || Num < 0 || Num > this.GetMaxNum()) {
|
||||
return undefined;
|
||||
}
|
||||
var BufRead = this.GetMap(Num);
|
||||
if (!BufRead) {
|
||||
BufRead = global.BufLib.GetNewBuffer(this.DataSize)
|
||||
var Position = Num * this.DataSize;
|
||||
var FI = this.OpenDBFile(this.FileName);
|
||||
var bytesRead = fs.readSync(FI.fd, BufRead, 0, BufRead.length, Position);
|
||||
if (bytesRead !== BufRead.length)
|
||||
return undefined;
|
||||
this.SetMap(Num, BufRead)
|
||||
}
|
||||
if (GetBufOnly) {
|
||||
return BufRead;
|
||||
}
|
||||
try {
|
||||
Data = global.BufLib.GetObjectFromBuffer(BufRead, this.Format, this.WorkStruct)
|
||||
}
|
||||
catch (e) {
|
||||
global.ToLog("DBROW:" + e)
|
||||
return undefined;
|
||||
}
|
||||
Data.Num = Num
|
||||
return Data;
|
||||
}
|
||||
GetRows(start, count) {
|
||||
var arr = [];
|
||||
for (var num = start; num < start + count; num++) {
|
||||
var Data = this.Read(num);
|
||||
if (!Data)
|
||||
break;
|
||||
arr.push(Data)
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
Truncate(LastNum) {
|
||||
var startTime = process.hrtime();
|
||||
LastNum = Math.trunc(LastNum)
|
||||
var Position = (LastNum + 1) * this.DataSize;
|
||||
if (Position < 0)
|
||||
Position = 0
|
||||
var FI = this.OpenDBFile(this.FileName, 1);
|
||||
if (Position < FI.size) {
|
||||
this.LastHash = undefined
|
||||
this.WasUpdate = 1
|
||||
if (LastNum < 0)
|
||||
global.ToLog("Truncate " + this.FileName + " from 0", 2)
|
||||
FI.size = Position
|
||||
fs.ftruncateSync(FI.fd, FI.size)
|
||||
this.BufMap = {}
|
||||
this.BufMapCount = 0
|
||||
}
|
||||
global.ADD_TO_STAT_TIME("ROWS_WRITE_MS", startTime)
|
||||
}
|
||||
DeleteHistory(BlockNumFrom) {
|
||||
var MaxNum = this.GetMaxNum();
|
||||
if (MaxNum === - 1)
|
||||
return;
|
||||
for (var num = MaxNum; num >= 0; num--) {
|
||||
var ItemCheck = this.Read(num);
|
||||
if (!ItemCheck)
|
||||
break;
|
||||
if (ItemCheck.BlockNum < BlockNumFrom) {
|
||||
if (num < MaxNum) {
|
||||
this.Truncate(num)
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.Truncate(- 1)
|
||||
}
|
||||
FastFindBlockNum(BlockNum) {
|
||||
var MaxNum = this.GetMaxNum();
|
||||
if (MaxNum === - 1)
|
||||
return;
|
||||
var StartNum = 0;
|
||||
var EndNum = MaxNum;
|
||||
var CurNum = Math.trunc(MaxNum / 2);
|
||||
while (true) {
|
||||
var Item = this.Read(CurNum);
|
||||
if (Item) {
|
||||
if (Item.BlockNum > BlockNum) {
|
||||
EndNum = CurNum - 1
|
||||
var Delta = CurNum - StartNum;
|
||||
if (Delta === 0)
|
||||
return "NoHistory";
|
||||
Delta = Math.trunc((1 + Delta) / 2)
|
||||
CurNum = CurNum - Delta
|
||||
}
|
||||
else
|
||||
if (Item.BlockNum < BlockNum) {
|
||||
StartNum = CurNum + 1
|
||||
var Delta = EndNum - CurNum;
|
||||
if (Delta === 0)
|
||||
return "NoPresent";
|
||||
Delta = Math.trunc((1 + Delta) / 2)
|
||||
CurNum = CurNum + Delta
|
||||
}
|
||||
else
|
||||
if (Item.BlockNum === BlockNum)
|
||||
break;
|
||||
}
|
||||
else {
|
||||
throw "Error read num";
|
||||
return;
|
||||
}
|
||||
}
|
||||
var num = CurNum;
|
||||
while (true) {
|
||||
num--
|
||||
if (num < 0)
|
||||
return CurNum;
|
||||
var Item = this.Read(num);
|
||||
if (Item) {
|
||||
if (Item.BlockNum === BlockNum)
|
||||
CurNum = num
|
||||
else
|
||||
return CurNum;
|
||||
}
|
||||
else {
|
||||
throw "Error read num";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
SetMap(Num, Value) {
|
||||
this.BufMap[Num] = Value
|
||||
this.BufMapCount++
|
||||
}
|
||||
GetMap(Num) {
|
||||
return this.BufMap[Num];
|
||||
}
|
||||
DeleteMap(Num) {
|
||||
if (this.BufMap[Num]) {
|
||||
delete this.BufMap[Num]
|
||||
this.BufMapCount--
|
||||
}
|
||||
}
|
||||
CheckBufMap() {
|
||||
if (this.BufMapCount > 1000) {
|
||||
this.ClearBufMap()
|
||||
}
|
||||
}
|
||||
ClearBufMap() {
|
||||
this.BufMap = {}
|
||||
this.BufMapCount = 0
|
||||
}
|
||||
Close() {
|
||||
this.ClearBufMap()
|
||||
this.CloseDBFile(this.FileName)
|
||||
}
|
||||
};
|
||||
106
src/core/db/db.ts
Normal file
106
src/core/db/db.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* @project: TERA
|
||||
* @version: Development (beta)
|
||||
* @license: MIT (not for evil)
|
||||
* @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com]
|
||||
* Web: https://terafoundation.org
|
||||
* Twitter: https://twitter.com/terafoundation
|
||||
* Telegram: https://t.me/terafoundation
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
import * as fs from 'fs'
|
||||
|
||||
module.exports = class {
|
||||
private DBMap;
|
||||
private LastHash;
|
||||
private WasUpdate;
|
||||
private WasCheckPathDB;
|
||||
|
||||
constructor() {
|
||||
this.DBMap = {}
|
||||
}
|
||||
CheckPathDB() {
|
||||
var Path = global.global.GetDataPath("DB");
|
||||
global.global.CheckCreateDir(Path)
|
||||
}
|
||||
CloseDBFile(name, bdelete) {
|
||||
this.LastHash = undefined
|
||||
this.WasUpdate = 1
|
||||
var Item = this.DBMap[name];
|
||||
if (Item) {
|
||||
let bDelete = bdelete;
|
||||
let Name = name;
|
||||
fs.close(Item.fd, function(err) {
|
||||
if (!err) {
|
||||
if (bDelete) {
|
||||
var fname = global.global.GetDataPath("DB/" + Name);
|
||||
fs.unlink(fname, function(err) {
|
||||
if (err)
|
||||
global.ToLog(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
else {
|
||||
global.ToLog(err)
|
||||
}
|
||||
})
|
||||
delete this.DBMap[name]
|
||||
}
|
||||
}
|
||||
OpenDBFile(name, bWrite, bExist) {
|
||||
if (bWrite && global.READ_ONLY_DB) {
|
||||
global.ToLogTrace("CANNOT WRITE - DB IN READ_ONLY MODE!!!")
|
||||
process.exit()
|
||||
}
|
||||
if (bWrite)
|
||||
CheckStartOneProcess(name + "-run")
|
||||
this.LastHash = undefined
|
||||
this.WasUpdate = 1
|
||||
var Item = this.DBMap[name];
|
||||
if (Item === undefined) {
|
||||
if (!this.WasCheckPathDB) {
|
||||
this.CheckPathDB()
|
||||
this.WasCheckPathDB = true
|
||||
}
|
||||
var fname = global.global.GetDataPath("DB/" + name);
|
||||
if (!fs.existsSync(fname)) {
|
||||
if (bExist) {
|
||||
this.DBMap[name] = null
|
||||
return null;
|
||||
}
|
||||
var fd = fs.openSync(fname, "w+");
|
||||
fs.closeSync(fd)
|
||||
}
|
||||
var fd = fs.openSync(fname, "r+");
|
||||
var stat = fs.statSync(fname);
|
||||
var size = stat.size;
|
||||
Item = { name: name, fname: fname, fd: fd, size: size, FillRows: 0, CountRows: 0, }
|
||||
this.DBMap[name] = Item
|
||||
}
|
||||
return Item;
|
||||
}
|
||||
};
|
||||
var MapCheckProcess = {};
|
||||
var BlockDB = new module.exports();
|
||||
|
||||
function CheckStartOneProcess(Name) {
|
||||
if (global.UpdateMode)
|
||||
return;
|
||||
if (global.READ_ONLY_DB || MapCheckProcess[Name])
|
||||
return;
|
||||
MapCheckProcess[Name] = 1;
|
||||
var path = global.global.GetDataPath("DB/" + Name);
|
||||
if (fs.existsSync(path)) {
|
||||
fs.unlinkSync(path);
|
||||
}
|
||||
try {
|
||||
BlockDB.OpenDBFile(Name);
|
||||
}
|
||||
catch (e) {
|
||||
global.ToLog("****** DETECT START ANOTHER PROCESS for: " + Name);
|
||||
global.ToLogTrace("EXIT");
|
||||
process.exit();
|
||||
}
|
||||
};
|
||||
global.CheckStartOneProcess = CheckStartOneProcess;
|
||||
Reference in New Issue
Block a user