Nodejs - нехватка памяти

У меня есть код для MySQL для записи данных в Excel и загрузки файла Excel с помощью nodejs, который занимает некоторое время, и после этого выдает ошибку nodejs-heap из памяти.

exports.exportdatatocsv = async (req, res) => {
  con.query(
    "SELECT sender_name, table_name FROM sender_tbl",
    function (error, data) {
      if (error) {
        console.error(error);
        res.status(500).send("Internal Server Error");
        return;
      }

      var mysqlData = JSON.parse(JSON.stringify(data));
      var workbook = new ExcelJS.stream.xlsx.WorkbookWriter({ stream: res });

      mysqlData.forEach((sender, index) => {
        var worksheet = workbook.addWorksheet(
          sender.sender_name.substring(0, 31)
        );

        con.query(
          `SELECT * FROM ${sender.table_name} ORDER BY id DESC`,
          function (error, tableData) {
            if (error) {
              console.error(error);
              res.status(500).send("Internal Server Error");
              return;
            }

            var fileHeader = [
              "message",
              "info",
              "credit/debit",
              "amount",
              "netbal",
            ];
            worksheet.addRow(fileHeader);

            tableData.forEach((row) => {
              worksheet.addRow([
                row.message,
                row.info,
                row.isdebit ? "debit" : "credit",
                row.amount,
                row.netbal,
              ]);
            });

            if (index === mysqlData.length - 1) {
              workbook
                .commit()
                .then(function () {
                  res.status(200).end();
                })
                .catch(function (err) {
                  console.error(err);
                  res
                    .status(500)
                    .send("Error occurred while generating Excel file");
                });
            }
          }
        );
      });
    }
  );
};

🤔 А знаете ли вы, что...
JavaScript позволяет создавать динамические и интерактивные веб-приложения.


62
1

Ответ:

Решено

Вероятно, во время операций вы достигли максимального доступного пространства кучи, у вас есть два возможных варианта:

  1. Оптимизируйте свой код для хранения и обработки данных из SQL через поток и канал преобразования, например с помощью генераторов.
  2. Увеличьте пространство кучи окружения вашего узла с помощью
node --max-old-space-size=2048 yourApp.js

NOTE: значение 2048 приведено только в качестве примера, попробуйте упорядочить его с помощью некоторых тестов, увеличивая или уменьшая его.

Оптимизированное решение

async function main() {
  let index = 0;
  let batchSize = 100;
  for await (const elem of processSenderTbl(batchSize)) {
    // elem is a list of batch
    const mysqlData = JSON.parse(JSON.stringify(elem));
    const workbook = new ExcelJS.stream.xlsx.WorkbookWriter({ stream: res });

    for (const sender of mysqlData) {
      const worksheet = workbook.addWorksheet(
        sender.sender_name.substring(0, 31)
      );

      for await (const tableData of processSenderTbl_(sender.table_name,batchSize)){
        var fileHeader = [
            "message",
            "info",
            "credit/debit",
            "amount",
            "netbal",
          ];
          worksheet.addRow(fileHeader);

          tableData.forEach((row) => {
            worksheet.addRow([
              row.message,
              row.info,
              row.isdebit ? "debit" : "credit",
              row.amount,
              row.netbal,
            ]);
          });

      }

      if (index === mysqlData.length - 1) {
        workbook
          .commit()
          .then(function () {
            res.status(200).end();
          })
          .catch(function (err) {
            console.error(err);
            res
              .status(500)
              .send("Error occurred while generating Excel file");
          });
      }
      
      index++
    }
  }
}
async function* processSenderTbl(batchSize: number) {
  let offset = 0;
  try {
    while (true) {
      // Fetch data with the current offset
      const query = `SELECT sender_name, table_name FROM sender_tbl LIMIT ? OFFSET ?`;
      const payload = [batchSize, offset];

      const [row, _] = await conn.query(query, payload);
      const results = row as any[];

      if (results.length === 0) {
        // No more data to fetch
        break;
      }

      yield results;
      offset += batchSize;
    }
  } catch (err) {
    console.info(err);
    return;
  }
}
async function* processSenderTbl_(tableName: string, batchSize: number) {
    let offset = 0;
    try {
      while (true) {
        // Fetch data with the current offset
        const query = `SELECT * FROM ?? LIMIT ? OFFSET ?`;
        const payload = [tableName, batchSize, offset];
        
        const [row, _] = await conn.query(query, payload);
        const results = row as any[];
  
        if (results.length === 0) {
          // No more data to fetch
          break;
        }
  
        yield results;
        offset += batchSize;
      }
    } catch (err) {
      console.info(err);
      return;
    }
}

Это всего лишь доказательство концепции, основанной на вашем коде, поскольку у меня нет всей вашей кодовой базы;

NOTE: Я использовал обещание в качестве стратегии подключения к базе данных, поэтому вы можете обновить информацию о соединении, добавив дополнительные promise(), я предполагаю, что вы используете mysql2 в качестве драйвера базы данных.

const conn = mysql2
  .createConnection({
    ...
  })
  .promise();

Дайте мне знать, если это поможет

Некоторые ресурсы:

Учебное пособие по генераторам Javascript

MDN - Ссылка