Перейти к основному содержимому

Batch-режим

Каждая изменяющая команда в Fabric это отдельная транзакция, а значит отдельный endorsement, рассылка и запись в блок. Когда команд много и каждая мелкая, накладные расходы на транзакцию начинают преобладать над самой работой. Batch-режим снимает эту проблему: несколько команд собираются и исполняются одной транзакцией. Мы сознательно жертвуем мгновенностью ответа ради пропускной способности и стоимости endorsement — ответ приходит позже, из блока.

Два приёмника​

На стороне chaincode за режим отвечает выбор приёмника. Обычный TransportFabricChaincodeReceiver исполняет команду сразу, а TransportFabricChaincodeReceiverBatch накапливает их и проводит пакетом. Выбор делается по наличию настроек batch — в NestJS-проекте прямо в фабрике провайдера:

useFactory: (logger: Logger) => {
let item: TransportFabricChaincodeReceiver = null;
if (_.isNil(settings.batch)) {
item = new TransportFabricChaincodeReceiver(logger, settings);
item.createStubFactory = (logger, stub, payload, transport) =>
new TransportFabricStubStateProxy(logger, stub, payload.id, payload.options, transport);
} else {
item = new TransportFabricChaincodeReceiverBatch(logger, settings);
}
return item;
}

Обычный приёмник здесь ещё и подменяет фабрику stub на TransportFabricStubStateProxy — тот самый внутритранзакционный кэш с отложенным commit, о котором речь в @hlf-core/transport-chaincode.

Конфигурация и подпись пакета​

Настройки batch — это таймаут сборки, алгоритм и публичный ключ, которым подписывается сам пакет. Ключ пакета отдельный, и приёмник сверяет его при приёме: пакет формирует доверенный batch-collector, а не произвольный клиент, поэтому подпись здесь не пользовательская. В боевом chaincode эти параметры читаются из окружения:

public get batch(): ITransportFabricSettingsBatch {
return this.isBatch ? {
timeout: this.getValue('FABRIC_BATCH_TIMEOUT', DateUtil.MILLISECONDS_SECOND),
algorithm: this.getValue('FABRIC_BATCH_ALGORITHM', TransportCryptoManagerEd25519.ALGORITHM),
publicKey: this.getValue('FABRIC_BATCH_PUBLIC_KEY', '...'),
} : null;
}

Подпись самих команд внутри пакета при этом остаётся прежней — пользовательской, с nonce и проверкой ключа. Batch добавляет над ней ещё один слой, а не заменяет её.

Клиент и ответы из блока​

На клиентской стороне batch-режиму отвечает TransportFabricBatch. Изменяющие команды в нём не ждут ответа поодиночке — chaincode исполнит их пакетом, а ответы придут позже, из блока, как карта «хэш команды → ответ». Разбирает блок TransportFabricBlockParserBatch, и по requestId клиент сопоставляет пришедший ответ с исходной командой. Readonly-команды в пакет не попадают — им это и не нужно, они и так исполняются чтением без транзакции.

Чтобы клиент не гадал, включён ли batch, chaincode отдаёт свои метаданные readonly-командой ChaincodeMetadataGetCommand — по ним видно режим и параметры пакета, и клиент выбирает нужный транспорт:

const metadata = await transport.sendListen(new ChaincodeMetadataGetCommand());
// metadata.mode, metadata.batch — по ним клиент решает, брать TransportFabric или TransportFabricBatch