Transaction 다량 발생 중 invalid null 에러 발생 질문!

안녕하세요? 평소에 "klaytn forum"을 통해 많은 도움을 받고 있습니다.

포럼 게시글 중 아래 링크

에 대한 글을 읽고, 노드에 전달되는 transaction들의 nonce ordering에 대한 확인 필요성을 느껴,
기존 코드에서 sendRawTransaction 하기 전에 nonce값들을 찍어봤더니 역시나 순차적이지 않았습니다.

그래서 코드를 아래와 같이 수정하여 sendRawTransaction 시 nonce는 반드시 순차적으로 들어가도록 하였습니다. 아래는 코드 전체 내용입니다.

/**
 * @file ExamMyERC20.js
 * @notice MyERC20 token deploy 동작 확인
 * @author jhhong
 */

//// COMMON
const colors = require('colors/safe'); // 콘솔 Color 출력
const fs     = require("fs"); // keystore 파일 처리를 위함
const path = require('path'); // .env 경로 추출을 위함
//// DOTENV
require('dotenv').config({ path: path.join(__dirname, './.env') }); // 지정된 경로의 환경변수 사용 (.env 파일 참조)
//// LOGs
const initLog   = require(`./libs/libWinston`).initLog; // 로그 초기화 함수 (winston)
const logToFile = require(`./libs/libWinston`).enableLogFile;
const Log       = require(`./libs/libWinston`).Log;
const RED       = require('./libs/libWinston.js').consoleRed; // 콘솔 컬러 출력: RED
const GREEN     = require('./libs/libWinston.js').consoleGreen; // 콘솔 컬러 출력: GREEN
//// caver
const Caver = require('caver-js'); 
const caver = new Caver(`http://${process.env.PROVIDER_INFO}`);
//// ABI & ByteCode
const abi      = require(`./build/contracts/MyERC20.json`).abi; // 컨트랙트 ABI
const byteCode = require(`./build/contracts/MyERC20.json`).bytecode; // 컨트랙트 bytecode
//// GLOBAL
let keyring  = undefined;
let nonce    = undefined;
let curNonce = undefined;
let txMap    = {};

/**
 * ms 단위로 딜레이를 준다.
 * @param {*} ms 딜레이 타임 (ms)
 */
let delay = function(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * @notice deploy-event 이벤트 처리함수
 * @param idx 인덱스 번호
 * @author jhhong
 */
process.on('deploy-event', function(index) {
    Log('DEBUG', `Receive deploy-event! index:[%s]`, index);
    if(nonce == undefined) {
        Log('DEBUG', `nonce does not initialized`);
        return;
    }
    let param = nonce++;
    process.emit('proc-deploy', index, param);
});

/**
 * @notice deploy tx 생성함수
 * @param keyring address keyring 정보
 * @param nonce address 논스값
 * @param index 호출 인덱스
 * @author jhhong
 */
let makeTxDeploy = async function(keyring, nonce, index) {
    try {
        let name = 'My ERC20 Token';
        let symbol = 'MET';
        let totalSupply = '100000000000000000000000000';
        let myErc20 = caver.contract.create(abi);
        let data = await myErc20.deploy({data: byteCode, arguments:[name, symbol, totalSupply]}).encodeABI();
        let gas = 2000000;
        let rawTx = { from: keyring.address, gas: gas, nonce: nonce, data: data };
        const tx = caver.transaction.smartContractDeploy.create(rawTx);
        await caver.wallet.sign(keyring.address, tx);
        Log('DEBUG', `생성!! Idx:[${index}], nonce:[${nonce}]`);
        let obj = new Object();
        obj.contents = tx;
        obj.created = true;
        obj.index = index;
        txMap[nonce] = obj;
    } catch(error) {
        console.log(colors.red(error));
        return false;
    }
}

/**
 * @notice sendKlay transaction 전송함수
 * @param nonce 트랜젝션 논스
 * @param obj 서명된 트랜젝션이 포함된 구조체
 * @author jhhong
 */
let sendTxDeploy = function(nonce, obj) {
    Log('DEBUG', `전송!!!!!!! Idx:[${obj.index}], nonce:[${nonce}]`);
    caver.rpc.klay.sendRawTransaction(obj.contents)
    .on('transactionHash', function(txHash) {
        Log('DEBUG', `[transactionHash] Idx:[${obj.index}], Deploy TX:['${GREEN(txHash)}']`);
    })
    .on('receipt', function(receipt) {
        Log('DEBUG', `[receipt] Idx:[${obj.index}], Deploy TX:['${GREEN(receipt.transactionHash)}'], BlockNumber:[${GREEN(parseInt(receipt.blockNumber))}]`);
        delete obj;
    });
}

/**
 * @notice 테스트 함수
 * @author jhhong
 */
let RunProc = async function() {
   try {
        await initLog();
        await logToFile(`deploy`);
        let passwd = `${process.env.PASSWORD}`;
        let keystore = await JSON.parse(fs.readFileSync('./keystore.json', 'utf-8'));
        if (keystore == null) {
            throw new Error("Keystore File does not exist!");
        }
        keyring = await caver.wallet.keyring.decrypt(keystore, passwd);
        nonce = await caver.rpc.klay.getTransactionCount(keyring.address);
        curNonce = parseInt(nonce);
        await caver.wallet.add(keyring);
        
        /**
         * @notice proce-deploy 이벤트 처리함수
         * @param idx 인덱스 번호
         * @param nonce keyring address 논스값
         * @author jhhong
         */
        process.on('proc-deploy', async function(idx, nonce){ 
            try {
                Log('DEBUG', `start proc-deploy :[${idx}], nonce:[${nonce}]`);
                await makeTxDeploy(keyring, nonce, idx);
                Log('DEBUG', `nonce:[${nonce}], curNonce:[${curNonce}]`);
                if(nonce == curNonce) {
                    while(txMap[curNonce]) {
                        sendTxDeploy(curNonce, txMap[curNonce]);
                        curNonce++;
                    }
                }
            } catch(error) {
                Log('ERROR', `${RED(error)}`);
            }
        }); 

        for(let i = 0; i < 2000; i++) {
            process.emit('deploy-event', i);
        }
        await delay(100000);
    } catch(error) {
        Log('ERROR', `${RED(error)}`);
    }
}
RunProc();

위 코드를 실행해본 결과 ordering이 정상적으로 이루어지는 건 확인하였으나, invalide response: null은 여전히 발생합니다.

2021-11-12 15:53:36 debug: <ExMyERC20Deploy2.js:91> 전송!!!!!!! Idx:[1999], nonce:[25164]
(node:2833) UnhandledPromiseRejectionWarning: Error: Invalid response: null
    at Object.InvalidResponse (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/caver-js/packages/caver-core-helpers/src/errors.js:90:37)
    at XMLHttpRequest.request.onreadystatechange (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/caver-js/packages/caver-core-requestmanager/caver-providers-http/src/index.js:103:32)
    at XMLHttpRequestEventTarget.dispatchEvent (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:22)
    at XMLHttpRequest._setReadyState (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request.js:208:14)
    at XMLHttpRequest._onHttpRequestError (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request.js:349:14)
    at ClientRequest.<anonymous> (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request.js:252:61)
    at ClientRequest.emit (events.js:376:20)
    at Socket.socketErrorListener (_http_client.js:475:9)
    at Socket.emit (events.js:376:20)
    at emitErrorNT (internal/streams/destroy.js:106:8)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:2833) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:2833) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:2833) UnhandledPromiseRejectionWarning: Error: Invalid response: null
    at Object.InvalidResponse (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/caver-js/packages/caver-core-helpers/src/errors.js:90:37)
    at XMLHttpRequest.request.onreadystatechange (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/caver-js/packages/caver-core-requestmanager/caver-providers-http/src/index.js:103:32)
    at XMLHttpRequestEventTarget.dispatchEvent (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:22)
    at XMLHttpRequest._setReadyState (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request.js:208:14)
    at XMLHttpRequest._onHttpRequestError (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request.js:349:14)
    at ClientRequest.<anonymous> (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request.js:252:61)
    at ClientRequest.emit (events.js:376:20)
    at Socket.socketErrorListener (_http_client.js:475:9)
    at Socket.emit (events.js:376:20)
    at emitErrorNT (internal/streams/destroy.js:106:8)
(node:2833) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:2833) UnhandledPromiseRejectionWarning: Error: Invalid response: null
    at Object.InvalidResponse (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/caver-js/packages/caver-core-helpers/src/errors.js:90:37)
    at XMLHttpRequest.request.onreadystatechange (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/caver-js/packages/caver-core-requestmanager/caver-providers-http/src/index.js:103:32)

위 문제의 원인에 대해 알고 싶습니다.
검토해주시면 감사하겠읍니다.

사용하는 caver-js 버전이 어떻게 되시나요?
그리고 어떤 노드를 사용하고 계신가요?

제가 네트워크 따로 구성한 이후, 주신 코드를 사용해서 코드가 동작할 수 있도록 최소한의 수정을 한 뒤에 돌려보니 2000번을 돌리면 The connection cannot be served because Server.Concurrency limit exceeded 에러가 발생해서 1000번으로 돌리니 에러없이 동작하는 것을 확인했습니다.

주신 에러는 노드로부터 비정상적인 응답값을 받았을 때 나오는 에러입니다.

안녕하세요?

답변 주셔서 감사드립니다.

caver-js 버전은 1.6.4 를 사용하고 있읍니다.

그리고 노드는 kscn으로 서비스체인을 구축해서 사용하고 있습니다.

아래는 kscn 버전정보를 캡쳐한 내용입니다.

root@Dkargo-Scn03:~# kscn version
Klaytn v1.6.2+38c63d495d
root@Dkargo-Scn03:~#

감사합니다.

추가로 제가 테스트했을 때도

The connection cannot be served because Server.Concurrency limit exceeded

에러가 간간히 올라오는 걸 확인했었는데요.
위 에러는 어떠한 경우에 나는 것이며, 해결방안이 있는지에 대해서도 문의 드립니다.

감사드립니다.

말씀해주신대로 kscn으로 1SCN을 하나 띄워서 네트워크를 구성하고 테스트를 해보았습니다만
위에서 말씀해주신 에러가 재현이 되지 않습니다.

그리고 The connection cannot be served because Server.Concurrency limit exceeded 에러는 동시에 처리 가능한 개수 이상으로 요청이 들어온 경우 발생합니다.
병렬적으로 처리하는 요청의 개수를 줄이면 해당 에러는 사라집니다.
2000번은 위 에러 때문에 동작하지 않아 1500번까지 테스트 해보았습니다만 문제없이 동작합니다.

caver 입장에서는 비어있는 응답값을 받은 경우 발생하는 에러로 다양한 경우가 존재할 수 있습니다.
에러가 재현이 되지 않는 상황에서는 더이상 주신 로그만으론 알 수 있는 정보가 없습니다.

만약 이전에 말씀해 주신 것처럼 txpool에 펜딩된 트랜잭션이 있다면 그 영향이 있을 수도 있으니 왜 펜딩되고 있는지 살펴보시기 바랍니다.

//// caver
const Caver = require('caver-js')
const caver = new Caver(`http://127.0.0.1:8551`)
//// ABI & ByteCode
const abi = require('./packages/caver-kct/src/kctHelper').kip7JsonInterface // 컨트랙트 ABI
const byteCode = require(`./packages/caver-kct/src/kctHelper`).kip7ByteCode // 컨트랙트 bytecode
//// GLOBAL
let keyring = undefined
let nonce = undefined
let curNonce = undefined
let txMap = {}

/**
 * ms 단위로 딜레이를 준다.
 * @param {*} ms 딜레이 타임 (ms)
 */
let delay = function(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}

/**
 * @notice deploy tx 생성함수
 * @param keyring address keyring 정보
 * @param nonce address 논스값
 * @param index 호출 인덱스
 * @author jhhong
 */
let makeTxDeploy = async function(keyring, nonce, index) {
    try {
        let name = 'My ERC20 Token'
        let symbol = 'MET'
        let decimals = 18
        let totalSupply = '100000000000000000000000000'
        let myErc20 = caver.contract.create(abi)
        let data = await myErc20.deploy({ data: byteCode, arguments: [name, symbol, decimals, totalSupply] }).encodeABI()
        let gas = 2000000
        let rawTx = { from: keyring.address, gas: gas, nonce: nonce, data: data }
        const tx = caver.transaction.smartContractDeploy.create(rawTx)
        await caver.wallet.sign(keyring.address, tx)
        console.log(`트랜잭션 생성 Idx:[${index}], nonce:[${nonce}]`)
        let obj = new Object()
        obj.contents = tx
        obj.created = true
        obj.index = index
        txMap[nonce] = obj
    } catch (error) {
        console.log(error)
        return false
    }
}

/**
 * @notice sendKlay transaction 전송함수
 * @param nonce 트랜젝션 논스
 * @param obj 서명된 트랜젝션이 포함된 구조체
 * @author jhhong
 */
let sendTxDeploy = function(nonce, obj) {
    console.log(`트랜잭션 전송 Idx:[${obj.index}], nonce:[${nonce}]`)
    caver.rpc.klay
        .sendRawTransaction(obj.contents)
        .on('transactionHash', function(txHash) {
            console.log(`[transactionHash] Idx:[${obj.index}], Deploy TX:['${txHash}']`)
        })
        .on('receipt', function(receipt) {
            console.log(
                'DEBUG',
                `[receipt] Idx:[${obj.index}], Deploy TX:['${receipt.transactionHash}'], BlockNumber:[${parseInt(receipt.blockNumber)}]`
            )
            delete obj
        })
}

process.on('deploy-event', function(index) {
    console.log(`Receive deploy-event! index:[%s]`, index)
    if (nonce == undefined) {
        console.log(`nonce does not initialized`)
        return
    }
    let param = nonce++
    process.emit('proc-deploy', index, param)
})

/**
 * @notice 테스트 함수
 * @author jhhong
 */
let RunProc = async function() {
    try {
        keyring = await caver.wallet.keyring.createFromPrivateKey('3614732543faf2726ffff58b3243ac8f653e047963edaa6b46b15bcbc6dcd9c4')
        nonce = await caver.rpc.klay.getTransactionCount(keyring.address)
        curNonce = parseInt(nonce)
        await caver.wallet.add(keyring)

        /**
         * @notice proce-deploy 이벤트 처리함수
         * @param idx 인덱스 번호
         * @param nonce keyring address 논스값
         * @author jhhong
         */
        process.on('proc-deploy', async function(idx, nonce) {
            try {
                console.log(`start proc-deploy :[${idx}], nonce:[${nonce}]`)
                await makeTxDeploy(keyring, nonce, idx)
                console.log(`nonce:[${nonce}], curNonce:[${curNonce}]`)
                if (nonce == curNonce) {
                    while (txMap[curNonce]) {
                        sendTxDeploy(curNonce, txMap[curNonce])
                        curNonce++
                    }
                }
            } catch (error) {
				console.log('ERROR', `${error}`)
				process.exit(1)
            }
        })

        for (let i = 0; i < 1500; i++) {
            process.emit('deploy-event', i)
        }
        await delay(100000)
    } catch (error) {
        console.log('ERROR', `${error}`)
		process.exit(1)
    }
}
RunProc()

안녕하세요?

답변 정말 감사드립니다.

말씀주신 아래 내용

만약 이전에 말씀해 주신 것처럼 txpool에 펜딩된 트랜잭션이 있다면 그 영향이 있을 수도 있으니 왜 펜딩되고 있는지 살펴보시기 바랍니다.

에 대해 확인된 바를 말씀드리면,
최초 nonce ordering 처리를 하지 않은 코드에서는 pending이 생겼으나, nonce ordering 처리한 이후 코드에서는 pending이 더이상 발생하지 않았습니다.

그리고 저의 개발환경에서 확인했을 때도 2000번 생성하면 무조건 발생한 게 아니었고, 2000번씩 몇번 반복해서 수행하다 보면 발생했었습니다.
수치가 크면 클수록 발생빈도가 높았던 것 같았는데 (4000번, 5000번 ~)
제가 다시 테스트하여 에러 발생빈도가 높은 수치를 확인하여 다시 말씀드리겠습니다.

아울러 궁금한 사항에 대해 질문을 드리려고 하는데요.

The connection cannot be served because Server.Concurrency limit exceeded 관련하여
클레이튼 노드(KEN or KSCN)에서 동시에 처리 가능한 max 개수를 혹시 알 수 있을까요?

감사합니다.

안녕하세요?

루프 4000번으로 설정하면 거의 발생하는 것 같습니다.

그리고 컨트랙트 deploy transaction이 아닌 일반 send klay transaction을 발생시켜도 4000번씩 발생시켰을 때 invalid response: null이 발생합니다.

send klay를 테스트한 이유는 혹시 transaction size가 영향을 미치는지 궁금해서였습니다.

아래에 send klay 전송 코드 또한 첨부드립니다.

/**
 * @file ExSendKlay.js
 * @notice klay 전송 동작 확인
 * @author jhhong
 */

//// COMMON
const colors = require('colors/safe'); // 콘솔 Color 출력
const fs     = require("fs"); // keystore 파일 처리를 위함
const path = require('path'); // .env 경로 추출을 위함
//// DOTENV
require('dotenv').config({ path: path.join(__dirname, './.env') }); // 지정된 경로의 환경변수 사용 (.env 파일 참조)
//// LOGs
const initLog   = require(`./libs/libWinston`).initLog; // 로그 초기화 함수 (winston)
const logToFile = require(`./libs/libWinston`).enableLogFile;
const Log       = require(`./libs/libWinston`).Log;
const RED       = require('./libs/libWinston.js').consoleRed; // 콘솔 컬러 출력: RED
const GREEN     = require('./libs/libWinston.js').consoleGreen; // 콘솔 컬러 출력: GREEN

//// caver
const Caver = require('caver-js'); 
const caver = new Caver(`http://${process.env.PROVIDER_INFO}`);
//// GLOBAL
let keyring  = undefined;
let nonce    = undefined;
let curNonce = undefined;
let txMap    = {};

/**
 * ms 단위로 딜레이를 준다.
 * @param {*} ms 딜레이 타임 (ms)
 */
let delay = function(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * @notice sendKlay tx 생성함수
 * @param keyring address keyring 정보
 * @param nonce address 논스값
 * @param index 호출 인덱스
 * @author jhhong
 */
process.on('send-klay', function(index) {
    Log('DEBUG', `Receive send-klay! index:[${index}]`);
    if(nonce == undefined) {
        Log('DEBUG', `nonce does not initialized`);
        return;
    }
    let param = nonce++;
    process.emit('proc-sendKlay', index, param);
});

/**
 * @notice sendKlay 테스트 함수
 * @author jhhong
 */
let makeTxSendKlay = async function(keyring, nonce, index) {
    try {
        let amount = 1000000000000000;
        let to = "0x38734f41f31e97365d65347e52ae9d9bd476871e";
        let gas = 100000;
        let rawTx = { from: keyring.address, to: to, gas: gas, nonce: nonce, value: amount };
        const tx = caver.transaction.valueTransfer.create(rawTx);
        await caver.wallet.sign(keyring.address, tx);
        Log('DEBUG', `생성!! Idx:[${index}], nonce:[${nonce}]`);
        let obj = new Object();
        obj.contents = tx;
        obj.created = true;
        obj.index = index;
        txMap[nonce] = obj;
    } catch(error) {
        console.log(colors.red(error));
        return false;
    }
}

/**
 * @notice sendKlay transaction 전송함수
 * @param nonce 트랜젝션 논스
 * @param obj 서명된 트랜젝션이 포함된 구조체
 * @author jhhong
 */
let sendTxSendKlay = function(nonce, obj) {
    Log('DEBUG', `전송!!!!!!! Idx:[${obj.index}], nonce:[${nonce}]`);
    caver.rpc.klay.sendRawTransaction(obj.contents)
    .on('transactionHash', function(txHash) {
        Log('DEBUG', `[transactionHash] Idx:[${obj.index}], Deploy TX:['${GREEN(txHash)}']`);
    })
    .on('receipt', function(receipt) {
        Log('DEBUG', `[receipt] Idx:[${obj.index}], Deploy TX:['${GREEN(receipt.transactionHash)}'], BlockNumber:[${GREEN(parseInt(receipt.blockNumber))}]`);
        delete obj;
    });
}

/**
 * @notice 테스트 함수
 * @author jhhong
 */
let RunProc = async function() {
   try {
        await initLog();
        await logToFile(`sendKlay`);
        let passwd = `${process.env.PASSWORD}`;
        let keystore = await JSON.parse(fs.readFileSync('./keystore.json', 'utf-8'));
        if (keystore == null) {
            throw new Error("Keystore File does not exist!");
        }
        keyring = await caver.wallet.keyring.decrypt(keystore, passwd);
        nonce = await caver.rpc.klay.getTransactionCount(keyring.address);
        curNonce = parseInt(nonce);
        Log('DEBUG', `curNonce:[${curNonce}]`);
        await caver.wallet.add(keyring);
        /**
         * @notice proce-sendKlay 이벤트 처리함수
         * @param idx 인덱스 번호
         * @param nonce keyring address 논스값
         * @author jhhong
         */
        process.on('proc-sendKlay', async function(idx, nonce){ 
            try {
                Log('DEBUG', `start proc-sendKlay :[${idx}], nonce:[${nonce}]`);
                await makeTxSendKlay(keyring, nonce, idx);
                Log('DEBUG', `nonce:[${nonce}], curNonce:[${curNonce}]`);
                if(nonce == curNonce) {
                    while(txMap[curNonce]) {
                        sendTxSendKlay(curNonce, txMap[curNonce]);
                        curNonce++;
                    }
                }
            } catch(error) {
                Log('ERROR', `${RED(error)}`);
            }
        }); 
        for(let i = 0; i < 4000; i++) {
            process.emit('send-klay', i);
        }
        await delay(100000);
    } catch(error) {
        Log('ERROR', `${RED(error)}`);
    }
}
RunProc();

감사합니다.

limit exceeded 에러는 http 관련 라이브러리에서 발생하는 에러입니다.
노드와 연관이 없습니다.
따로 limit을 설정할 수 있는 옵션값을 제공하고 있지 않는 것으로 확인됩니다.

그리고 제가 실행하면 2000번 이상부터는 limit exceeded 에러만 발생하는데요;

안녕하세요?

저의 테스트 결과와 관리자님의 테스트결과가 왜 다르게 나오는지에 대해 의구심을 가지고 살펴보던 중에
provider를 local로 구축해서 테스트해보니 저도 관리자님과 동일하게 2000개 transaction 생성 시 “The connection cannot be served because Server.Concurrency limit exceeded” 가 발생되었습니다.

제가 기존에 테스트 하던 환경은 provider가 cloud 환경에 있었습니다.

혹시 괜찬으시다면, 저희 cloud에 설치된 node로 연결해서 확인 해 주실 수 있으신지요?
제가 관리자님께 따로 DM 드리겠습니다.

감사합니다.