qvdjs 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -4
- package/dist/index.cjs +47 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +46 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import crypto from 'crypto';
|
|
|
4
4
|
import xml2 from 'xml2js';
|
|
5
5
|
import assert from 'assert';
|
|
6
6
|
import os from 'os';
|
|
7
|
+
import v8 from 'v8';
|
|
7
8
|
|
|
8
9
|
var __defProp = Object.defineProperty;
|
|
9
10
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -647,6 +648,9 @@ var init_bitUtils = __esm({
|
|
|
647
648
|
"src/util/bitUtils.js"() {
|
|
648
649
|
}
|
|
649
650
|
});
|
|
651
|
+
function getHeapLimit() {
|
|
652
|
+
return v8.getHeapStatistics().heap_size_limit;
|
|
653
|
+
}
|
|
650
654
|
function estimateMemoryUsage(symbolTableSize, maxRows, totalRows) {
|
|
651
655
|
const FULL_PARSE_OVERHEAD = 6;
|
|
652
656
|
const MINIMAL_OVERHEAD = 0.01;
|
|
@@ -659,11 +663,12 @@ function estimateMemoryUsage(symbolTableSize, maxRows, totalRows) {
|
|
|
659
663
|
const skippedSymbolsMemory = symbolTableSize * (1 - symbolPercentage) * MINIMAL_OVERHEAD;
|
|
660
664
|
return keptSymbolsMemory + skippedSymbolsMemory;
|
|
661
665
|
}
|
|
662
|
-
function validateMemoryAvailability(symbolTableSize, maxRows, totalRows, filePath) {
|
|
666
|
+
function validateMemoryAvailability(symbolTableSize, maxRows, totalRows, filePath, safetyFactor = 0.3) {
|
|
663
667
|
const availableMemory = os.freemem();
|
|
668
|
+
const heapLimit = getHeapLimit();
|
|
664
669
|
const estimatedMemory = estimateMemoryUsage(symbolTableSize, maxRows, totalRows);
|
|
665
|
-
const
|
|
666
|
-
const maxAllowedMemory =
|
|
670
|
+
const effectiveLimit = Math.min(availableMemory, heapLimit);
|
|
671
|
+
const maxAllowedMemory = effectiveLimit * safetyFactor;
|
|
667
672
|
if (estimatedMemory > maxAllowedMemory) {
|
|
668
673
|
const safeSymbolPercentage = maxAllowedMemory / (symbolTableSize * 6);
|
|
669
674
|
const safeRowPercentage = Math.pow(safeSymbolPercentage, 2);
|
|
@@ -671,14 +676,20 @@ function validateMemoryAvailability(symbolTableSize, maxRows, totalRows, filePat
|
|
|
671
676
|
const sizeMB = Math.round(symbolTableSize / 1024 / 1024);
|
|
672
677
|
const estimatedMB = Math.round(estimatedMemory / 1024 / 1024);
|
|
673
678
|
const availableMB = Math.round(maxAllowedMemory / 1024 / 1024);
|
|
679
|
+
const heapLimitMB = Math.round(heapLimit / 1024 / 1024);
|
|
680
|
+
const availableRamMB = Math.round(availableMemory / 1024 / 1024);
|
|
681
|
+
const limitingFactor = heapLimit < availableMemory ? "V8 heap limit" : "available RAM";
|
|
674
682
|
throw new QvdValidationError(
|
|
675
|
-
`Insufficient memory to load file safely. Symbol table: ${sizeMB}MB, Estimated memory needed: ${estimatedMB}MB, Available: ${availableMB}MB. Try loading fewer rows using the maxRows parameter (recommended: ${recommendedMaxRows.toLocaleString()} rows or less).`,
|
|
683
|
+
`Insufficient memory to load file safely. Symbol table: ${sizeMB}MB, Estimated memory needed: ${estimatedMB}MB, Available: ${availableMB}MB (limited by ${limitingFactor}: ${heapLimitMB}MB heap / ${availableRamMB}MB RAM). Try loading fewer rows using the maxRows parameter (recommended: ${recommendedMaxRows.toLocaleString()} rows or less).`,
|
|
676
684
|
{
|
|
677
685
|
file: filePath,
|
|
678
686
|
symbolTableSize,
|
|
679
687
|
symbolTableSizeMB: sizeMB,
|
|
680
688
|
estimatedMemoryMB: estimatedMB,
|
|
681
689
|
availableMemoryMB: availableMB,
|
|
690
|
+
heapLimitMB,
|
|
691
|
+
availableRamMB,
|
|
692
|
+
limitingFactor,
|
|
682
693
|
totalRows,
|
|
683
694
|
maxRows,
|
|
684
695
|
recommendedMaxRows
|
|
@@ -687,13 +698,15 @@ function validateMemoryAvailability(symbolTableSize, maxRows, totalRows, filePat
|
|
|
687
698
|
}
|
|
688
699
|
}
|
|
689
700
|
function warnLargeSymbolTable(symbolTableSize, maxRows, totalRows) {
|
|
690
|
-
const
|
|
701
|
+
const heapLimit = getHeapLimit();
|
|
702
|
+
const LARGE_SYMBOL_TABLE_WARNING = heapLimit * 0.125;
|
|
691
703
|
if (symbolTableSize > LARGE_SYMBOL_TABLE_WARNING && (maxRows === null || maxRows >= totalRows)) {
|
|
692
704
|
const sizeMB = Math.round(symbolTableSize / 1024 / 1024);
|
|
693
705
|
const estimatedMemory = estimateMemoryUsage(symbolTableSize, maxRows, totalRows);
|
|
694
706
|
const estimatedMB = Math.round(estimatedMemory / 1024 / 1024);
|
|
707
|
+
const warnMB = Math.round(LARGE_SYMBOL_TABLE_WARNING / 1024 / 1024);
|
|
695
708
|
console.warn(
|
|
696
|
-
`\u26A0\uFE0F Large symbol table detected (${sizeMB}MB). Loading all ${totalRows.toLocaleString()} rows will use ~${estimatedMB}MB RAM. Consider using the maxRows parameter for better performance and lower memory usage.`
|
|
709
|
+
`\u26A0\uFE0F Large symbol table detected (${sizeMB}MB > ${warnMB}MB threshold). Loading all ${totalRows.toLocaleString()} rows will use ~${estimatedMB}MB RAM. Consider using the maxRows parameter for better performance and lower memory usage.`
|
|
697
710
|
);
|
|
698
711
|
}
|
|
699
712
|
}
|
|
@@ -705,35 +718,43 @@ var init_memoryUtils = __esm({
|
|
|
705
718
|
|
|
706
719
|
// src/util/validationUtils.js
|
|
707
720
|
function validateSymbolTableSizeEarly(symbolTableLength, filePath) {
|
|
708
|
-
const
|
|
721
|
+
const heapLimit = getHeapLimit();
|
|
722
|
+
const MAX_SYMBOL_TABLE_SIZE = heapLimit * 0.125;
|
|
709
723
|
if (symbolTableLength > MAX_SYMBOL_TABLE_SIZE) {
|
|
710
724
|
const sizeMB = Math.round(symbolTableLength / 1024 / 1024);
|
|
711
725
|
const maxMB = Math.round(MAX_SYMBOL_TABLE_SIZE / 1024 / 1024);
|
|
726
|
+
const heapMB = Math.round(heapLimit / 1024 / 1024);
|
|
712
727
|
throw new QvdValidationError(
|
|
713
|
-
`Symbol table too large (${sizeMB}MB exceeds ${maxMB}MB limit). This QVD file contains extremely high-cardinality fields.
|
|
728
|
+
`Symbol table too large (${sizeMB}MB exceeds ${maxMB}MB limit for lazy loading). This QVD file contains extremely high-cardinality fields. Limit scales with heap size (current: ${heapMB}MB, limit: 12.5% = ${maxMB}MB). Consider: (1) loading the full file without maxRows, (2) increasing heap size with --max-old-space-size, or (3) aggregating high-cardinality fields.`,
|
|
714
729
|
{
|
|
715
730
|
file: filePath,
|
|
716
731
|
symbolTableSize: symbolTableLength,
|
|
717
732
|
symbolTableSizeMB: sizeMB,
|
|
718
733
|
maxAllowed: MAX_SYMBOL_TABLE_SIZE,
|
|
719
|
-
maxAllowedMB: maxMB
|
|
734
|
+
maxAllowedMB: maxMB,
|
|
735
|
+
heapLimitMB: heapMB,
|
|
736
|
+
limitPercentage: 12.5
|
|
720
737
|
}
|
|
721
738
|
);
|
|
722
739
|
}
|
|
723
740
|
}
|
|
724
741
|
function validateSymbolTableSize(symbolTableLength, filePath, totalRows) {
|
|
725
|
-
const
|
|
742
|
+
const heapLimit = getHeapLimit();
|
|
743
|
+
const ABSOLUTE_MAX_SYMBOL_TABLE = heapLimit * 0.5;
|
|
726
744
|
if (symbolTableLength > ABSOLUTE_MAX_SYMBOL_TABLE) {
|
|
727
745
|
const sizeMB = Math.round(symbolTableLength / 1024 / 1024);
|
|
728
746
|
const maxMB = Math.round(ABSOLUTE_MAX_SYMBOL_TABLE / 1024 / 1024);
|
|
747
|
+
const heapMB = Math.round(heapLimit / 1024 / 1024);
|
|
729
748
|
throw new QvdValidationError(
|
|
730
|
-
`Symbol table exceeds absolute maximum size (${sizeMB}MB > ${maxMB}MB). This QVD file has pathological cardinality (likely a data modeling issue).
|
|
749
|
+
`Symbol table exceeds absolute maximum size (${sizeMB}MB > ${maxMB}MB). This QVD file has pathological cardinality (likely a data modeling issue). Limit scales with heap size (current: ${heapMB}MB, limit: 50% = ${maxMB}MB). Consider: (1) increasing heap size with --max-old-space-size, (2) aggregating high-cardinality fields, (3) splitting the data, or (4) using a different format.`,
|
|
731
750
|
{
|
|
732
751
|
file: filePath,
|
|
733
752
|
symbolTableSize: symbolTableLength,
|
|
734
753
|
symbolTableSizeMB: sizeMB,
|
|
735
754
|
maxAllowed: ABSOLUTE_MAX_SYMBOL_TABLE,
|
|
736
755
|
maxAllowedMB: maxMB,
|
|
756
|
+
heapLimitMB: heapMB,
|
|
757
|
+
limitPercentage: 50,
|
|
737
758
|
totalRows
|
|
738
759
|
}
|
|
739
760
|
);
|
|
@@ -870,6 +891,7 @@ function validateFieldBitMetadata(field, recordSize, filePath) {
|
|
|
870
891
|
var init_validationUtils = __esm({
|
|
871
892
|
"src/util/validationUtils.js"() {
|
|
872
893
|
init_QvdErrors();
|
|
894
|
+
init_memoryUtils();
|
|
873
895
|
}
|
|
874
896
|
});
|
|
875
897
|
|
|
@@ -1152,10 +1174,14 @@ var init_QvdFileReader = __esm({
|
|
|
1152
1174
|
* @param {Object} [options={}] Options for the reader.
|
|
1153
1175
|
* @param {string} [options.allowedDir] Optional allowed directory path. If provided, the file
|
|
1154
1176
|
* path must be within this directory. Defaults to current working directory.
|
|
1177
|
+
* @param {number} [options.memorySafetyFactor=0.3] Memory safety factor (0.0-1.0). Determines
|
|
1178
|
+
* what percentage of available memory (or V8 heap limit, whichever is smaller) can be used
|
|
1179
|
+
* for loading QVD files. Default is 0.3 (30%). Increase for larger heap configurations.
|
|
1155
1180
|
*/
|
|
1156
1181
|
constructor(filePath, options = {}) {
|
|
1157
|
-
const { allowedDir } = options;
|
|
1182
|
+
const { allowedDir, memorySafetyFactor = 0.3 } = options;
|
|
1158
1183
|
this._path = validatePath(filePath, allowedDir);
|
|
1184
|
+
this._memorySafetyFactor = memorySafetyFactor;
|
|
1159
1185
|
this._buffer = null;
|
|
1160
1186
|
this._headerOffset = null;
|
|
1161
1187
|
this._symbolTableOffset = null;
|
|
@@ -1379,7 +1405,7 @@ var init_QvdFileReader = __esm({
|
|
|
1379
1405
|
const symbolTableSize = symbolBuffer.length;
|
|
1380
1406
|
const totalRows = parseInt(this._header["QvdTableHeader"]["NoOfRecords"], 10);
|
|
1381
1407
|
validateSymbolTableSize(symbolTableSize, this._path, totalRows);
|
|
1382
|
-
validateMemoryAvailability(symbolTableSize, maxRows, totalRows, this._path);
|
|
1408
|
+
validateMemoryAvailability(symbolTableSize, maxRows, totalRows, this._path, this._memorySafetyFactor);
|
|
1383
1409
|
warnLargeSymbolTable(symbolTableSize, maxRows, totalRows);
|
|
1384
1410
|
if (!Array.isArray(fields)) {
|
|
1385
1411
|
fields = [fields];
|
|
@@ -1960,11 +1986,17 @@ var init_QvdDataFrame = __esm({
|
|
|
1960
1986
|
* @param {Object} [options] Optional loading options.
|
|
1961
1987
|
* @param {number|null} [options.maxRows] The maximum number of rows to load. If not specified, all rows are loaded.
|
|
1962
1988
|
* @param {string} [options.allowedDir] Optional allowed directory path. If provided, the file path must be within this directory.
|
|
1989
|
+
* @param {number} [options.memorySafetyFactor=0.3] Memory safety factor (0.0-1.0). Determines what percentage
|
|
1990
|
+
* of available memory (or V8 heap limit, whichever is smaller) can be used. Default is 0.3 (30%).
|
|
1991
|
+
* Increase this (e.g., to 0.5 or 0.7) when running with larger heap sizes via --max-old-space-size.
|
|
1963
1992
|
* @return {Promise<QvdDataFrame>} The data frame of the QVD file.
|
|
1964
1993
|
*/
|
|
1965
1994
|
static async fromQvd(path3, options = {}) {
|
|
1966
1995
|
const { QvdFileReader: QvdFileReader2 } = await Promise.resolve().then(() => (init_QvdFileReader(), QvdFileReader_exports));
|
|
1967
|
-
const readerOptions = {
|
|
1996
|
+
const readerOptions = {
|
|
1997
|
+
allowedDir: options.allowedDir,
|
|
1998
|
+
memorySafetyFactor: options.memorySafetyFactor
|
|
1999
|
+
};
|
|
1968
2000
|
return await new QvdFileReader2(path3, readerOptions).load(options.maxRows !== void 0 ? options.maxRows : null);
|
|
1969
2001
|
}
|
|
1970
2002
|
/**
|