如何解决由于执行存储过程而导致的内存不足错误?

时间:2020-09-30 12:48:38

标签: mysql stored-procedures snowflake-cloud-data-platform

我正在尝试执行以下雪花存储过程:

数据最初被加载到登台数据库中的表中。从临时表中,我将数据加载到临时表中,然后再将主表与临时表交换,这可以在方法中找到:temp_table_insert_sql

成功完成此操作后,将使用getSwapTableStmt方法进行交换。

下面是我要执行的存储过程。

CREATE OR REPLACE PROCEDURE "DBNAME"."SCHEMA"."FULL_LOAD_WITH_RETRY_FACILITY"(RETRY_CNT FLOAT, MIN_WAIT_SECOND FLOAT, MAX_WAIT_SECOND FLOAT, TABLE_NAME VARCHAR, TEMPDB VARCHAR, TEMPSCHEMA VARCHAR, TARGETDB VARCHAR, TARGETSCHEMA VARCHAR)
RETURNS VARCHAR(16777216)
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS $$

    function getRandom(min,max){
        return Math.floor(Math.random()*(max-min+1))+min;
    }

    function create_temp_table(table_name) {
        var create_temp_table = `CREATE OR REPLACE TABLE ${TEMPDB}.${TEMPSCHEMA}.${table_name}_TEMP like ${TARGETDB}.${TARGETSCHEMA}.${table_name};`;
        return create_temp_table;
    }

    function retry(MIN_WAIT_SECOND, MAX_WAIT_SECOND, retry_cnt, stmt) {
        for (cnt=0; cnt<retry_cnt; cnt++) {
            try {
                var result = stmt.execute();
                break;
            } catch (err) {
                var get_error_queryid_result = snowflake.createStatement( { sqlText:`SELECT LAST_QUERY_ID();` } ).execute();
                get_error_queryid_result.next();
                var queryId = String( get_error_queryid_result.getColumnValue(1) ).trim();
                snowflake.createStatement( { sqlText: `SELECT SYSTEM$WAIT(${getRandom(MIN_WAIT_SECOND,MAX_WAIT_SECOND)});` } ).execute();
                var err_msg = err.message;
                if(!(err_msg.includes("locked")) | (cnt==(retry_cnt-1)) ){
                    err.stackTraceTxt += ` QueryId: ${queryId}`;
                    throw err;
                }
            }
        }
        return result;
    }

    function temp_table_insert_sql(table_name) {
        var select_columns = "";
        var insert_columns = "";
        var sql = `select column_name from ${TARGETDB}.INFORMATION_SCHEMA.COLUMNS where true and table_schema = '${TARGETSCHEMA}' and table_name = '${table_name}' order by ordinal_position;`;
        var result = snowflake.createStatement( { sqlText: sql } ).execute();
        if (result.next()) {
            insert_columns = `( ${String(result.getColumnValue(1).trim)}`;
            select_columns = `select ${String(result.getColumnValue(1).trim)}`;
            while(result.next) {
                insert_columns += `, ${String(result.getColumnValue(1).trim)}`;
                select_columns += `, ${String(result.getColumnValue(1).trim)}`;
            }
            insert_columns += `)`;
        } else {
            insert_columns = "";
        }
        return `CREATE OR REPLACE TABLE ${TEMPDB}.${TEMPSCHEMA}.${table_name}_TEMP AS ${select_columns} from ${TEMPDB}.${TEMPSCHEMA}.${table_name}_STG`;
    }

    function get_row_count(db, SCHEMA, table_name) {
        return `select count(*) from ${db}.${schema}.${table_name};`;
    }

    function getSwapTableStmt(table_name){
        return `alter table if exists ${TARGETDB}.${TARGETSCHEMA}.${table_name} swap with ${TEMPDB}.${TEMPSCHEMA}.${table_name}_TEMP;`;
    }
    
    try {
        var timest = new Date().getTime();
        var temp_row_count = 0;
        var stg_row_count = 0;
        var apptrace = "";
        var logs = "";
        var result = "";
        var sql = "";

        var create_temp_ddl = create_temp_table(TABLE_NAME);
        logs = create_temp_ddl;
        result = retry(MIN_WAIT_SECOND, MAX_WAIT_SECOND, RETRY_CNT, snowflake.createStatement({sqlText: create_temp_ddl}));

        var insert_into_temp_table = temp_table_insert_sql(TABLE_NAME);
        result = retry(MIN_WAIT_SECOND, MAX_WAIT_SECOND, RETRY_CNT, snowflake.createStatement({ sqlText: insert_into_temp_table }));
        
        var temprows = get_row_count(TEMPDB, TABLE_NAME + "_TEMP");
        result = retry(MIN_WAIT_SECOND, MAX_WAIT_SECOND, RETRY_CNT, snowflake.createStatement({ sqlText: temprows }));
        if (result.next()) {
            temp_row_count;
        } else {
            var err = new Error("rows inserted is zero");
            throw err;
        }

        var stgrows = get_row_count(TEMPDB, TABLE_NAME + "_STG");
        result = retry(MIN_WAIT_SECOND, MAX_WAIT_SECOND, RETRY_CNT, snowflake.createStatement({ sqlText: stgrows }));
        if (result.next()) {
            stg_row_count = parseInt(result.getColumnValue(1));
        } else {
            var err = new Error("rows inserted is zero");
            throw err;
        }

        logs = `SWAP ${TABLE_NAME}_TEMP ${TABLE_NAME};`;
        if(stg_row_count == temp_row_count) {
            sql = getSwapTableStmt(table_name);
        } else {
            var err = new Error(`rows of ${TABLE_NAME}_STG & ${TABLE_NAME}_TEMP are different`);
            throw err;
        }
        var duration = (new Date().getTime() - timest) / 1000;
        apptrace = `{
            "application_name": "FULL_LOAD"
            ,"feature_name": "exchange|3.0|SESSION|FILE|${TABLE_NAME}_STG|${TABLE_NAME}"
            ,"event_subtype": "Metric"
            ,"metrics": [
                {
                    "metric":"Execution_Result"
                    ,"measurement":1
                    ,"unit_of_measure":"Boolean"
                }
                ,{
                    "metric": "Execution_Duration"
                    ,"measurement": ${duration}
                    ,"unit_of_measure": "Seconds"
                }
                ,{
                    "metric": "Rows"
                    ,"measurement": ${temp_row_count}
                    ,"unit_of_measure": "Rows"
                }
            ]
        }`;
    } catch (err)  {
    logs += "  Code: " + err.code + "  State: " + err.state;
    logs += "  Message: " + err.message;
    apptrace = `{
        "application_name": "FULL_LOAD"
        ,"feature_name": "exchange|3.0|SESSION|FILE|${TABLE_NAME}_STG|${TABLE_NAME}|${logs}"
        ,"event_subtype": "Metric"
        ,"metrics": [
            {
                "metric":"Execution_Result"
                ,"measurement":0
                ,"unit_of_measure":"Boolean"
            }
        ]
    }`;
    }
    // COMMIT
    snowflake.execute (
        {sqlText: `COMMIT;`}
    );
    return apptrace;
$$
;

这是我调用存储过程的方式:

CALL DBNAME.SCHEMANAME.PROCNAME(2, 1000, 5000, 'TABLENAME', 'TEMPDB', 'TEMPSCHEMA', 'TARGETDB ', 'TARGETSCHEMA');

提交上述声明后,我会收到错误消息:

org.jkiss.dbeaver.model.sql.DBSQLException:SQL错误[100176] [P0000]:JavaScript内存不足错误:超出了UDF线程内存限制

在org.jkiss.dbeaver.model.impl.jdbc.exec.JDBCPreparedStatementImpl.executeStatement(JDBCPreparedStatementImpl.java:208)
在org.jkiss.dbeaver.ui.editors.sql.execute.SQLQueryJob.executeStatement(SQLQueryJob.java:492)
在org.jkiss.dbeaver.ui.editors.sql.execute.SQLQueryJob.lambda $ 0(SQLQueryJob.java:427)
在org.jkiss.dbeaver.model.exec.DBExecUtils.tryExecuteRecover(DBExecUtils.java:170)
在org.jkiss.dbeaver.ui.editors.sql.execute.SQLQueryJob.executeSingleQuery(SQLQueryJob.java:419)
在org.jkiss.dbeaver.ui.editors.sql.execute.SQLQueryJob.extractData(SQLQueryJob.java:779)
在org.jkiss.dbeaver.ui.editors.sql.SQLEditor $ QueryResultsContainer.readData(SQLEditor.java:2973)
在org.jkiss.dbeaver.ui.controls.resultset.ResultSetJobDataRead.lambda $ 0(ResultSetJobDataRead.java:111)
在org.jkiss.dbeaver.model.exec.DBExecUtils.tryExecuteRecover(DBExecUtils.java:170)
在org.jkiss.dbeaver.ui.controls.resultset.ResultSetJobDataRead.run(ResultSetJobDataRead.java:109)
在org.jkiss.dbeaver.ui.controls.resultset.ResultSetViewer $ 17.run(ResultSetViewer.java:3584)
在org.jkiss.dbeaver.model.runtime.AbstractJob.run(AbstractJob.java:104)
在org.eclipse.core.internal.jobs.Worker.run(Worker.java:63)

由以下原因引起:net.snowflake.client.jdbc.SnowflakeSQLException:JavaScript内存不足错误:UDF线程内存限制已超出

在net.snowflake.client.jdbc.SnowflakeUtil.checkErrorAndThrowExceptionSub(SnowflakeUtil.java:153)
在net.snowflake.client.jdbc.SnowflakeUtil.checkErrorAndThrowException(SnowflakeUtil.java:77)
在net.snowflake.client.core.StmtUtil.pollForOutput(StmtUtil.java:503)
在net.snowflake.client.core.StmtUtil.execute(StmtUtil.java:380)
在net.snowflake.client.core.SFStatement.executeHelper(SFStatement.java:582)
在net.snowflake.client.core.SFStatement.executeQueryInternal(SFStatement.java:266)
在net.snowflake.client.core.SFStatement.executeQuery(SFStatement.java:202)
位于net.snowflake.client.core.SFStatement.execute(SFStatement.java:877)
在net.snowflake.client.jdbc.SnowflakeStatementV1.executeInternal(SnowflakeStatementV1.java:331)
在net.snowflake.client.jdbc.SnowflakePreparedStatementV1.execute(SnowflakePreparedStatementV1.java:535)
在org.jkiss.dbeaver.model.impl.jdbc.exec.JDBCPreparedStatementImpl.execute(JDBCPreparedStatementImpl.java:261)
在org.jkiss.dbeaver.model.impl.jdbc.exec.JDBCPreparedStatementImpl.executeStatement(JDBCPreparedStatementImpl.java:205)
...另外12个

我正在运行此存储过程的数据库是Snowflake

这是我第一次处理存储过程和Java脚本。我不了解是什么导致了此错误,以及如何分析此错误。谁能让我知道如何解决此问题?真的很感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

Snowflake中的JavaScript UDFS可以消耗的内存有限制: https://docs.snowflake.com/en/sql-reference/udf-js.html#memory

如果您尝试简化查询或将一个事务拆分为多个事务,则错误可能不会出现。 (这在一个案例中对我有帮助)

也许此参数可能对您有帮助:https://docs.snowflake.com/en/sql-reference/parameters.html#client-memory-limit

答案 1 :(得分:0)

调试问题后,我发现代码中的while循环将变为无限,并导致内存不足异常。我修复了循环后,错误得以解决。