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/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 MEMORY_SAFETY_FACTOR = 0.3;
666
- const maxAllowedMemory = availableMemory * MEMORY_SAFETY_FACTOR;
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 LARGE_SYMBOL_TABLE_WARNING = 500 * 1024 * 1024;
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 MAX_SYMBOL_TABLE_SIZE = 500 * 1024 * 1024;
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. Even with symbol filtering, this file cannot be loaded efficiently. Consider: (1) using a subset of the data, (2) aggregating high-cardinality fields, or (3) using a different data format.`,
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 ABSOLUTE_MAX_SYMBOL_TABLE = 2 * 1024 * 1024 * 1024;
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). Even with Phase 2.5 optimization, files this large cannot be processed safely. Consider: (1) aggregating high-cardinality fields, (2) splitting the data, or (3) using a different format.`,
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 = { allowedDir: options.allowedDir };
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
  /**