I am facing a performance issue while using Hyperledger fabric node.js sdk.
When I issue the invocation request to sdk and consume the response given by the chaincode by using the following code
var proposalResponse = results[0];
var proposal = results[1];
let isProposalGood = false;
if(proposalResponse
&& proposalResponse[0].response
&& proposalResponse[0].response.status === 200){
isProposalGood = true;
var res = JSON.parse(proposalResponse[0].response.payload.toString());
res.event_status = 'VALID'
resolve(res);
}else{
reject(createErrorResponse(proposalResponse[0].message,500))
return
}
The api responds within 50ms as you can see the screenshot below:
But, when I wait for orderer to confirm the transaction by using the following code:
if (code === 'VALID'){
//get payload from proposal response
var res = JSON.parse(proposalResponse[0].response.payload.toString());
res.event_status = 'VALID'
resolve(res);
}else{
var return_status = createErrorResponse("Transaction was not valid", 500);
return_status.tx_id = transaction_id_string
reject(return_status)
return
}
It takes nearby 2500ms to response as you can see the screenshot of postman below:
Correct me if I am wrong
I know it takes time because the orderer confirms the transaction and commits into the ledger. But don't you think we should proceed only if the orderer agrees to transaction and commits into the ledger. If yes, then it will take 2.5 seconds to response (network is running on docker in local machine & sdk on same machine) which is a performance issue.
What happen if data is written into the chaincode and after that orderers deny to write the transaction into the ledger?
Any help would be appreciated
the orderer confirms the transaction and commits into the ledger.
The task for the Ordering service (As the name suggests) is only to order the received endorsed transactions chronologically by channel and then deliver them to all the peers in the channel. Orderers don't actually commit the transactions into the ledger.
The Committer Peers do. And committing is a time-taking process since all the peers validate all the transactions within the block to ensure endorsement policy is fulfilled and to ensure that there have been no changes to ledger state for read set variables since the read set was generated by the transaction execution. Transactions in the block are tagged as being valid or invalid. Then Each peer appends the block to the channel’s chain, and for each valid transaction the write sets are committed to current state database. An event is emitted, to notify the client application that the transaction (invocation) has been immutably appended to the chain, as well as notification of whether the transaction was validated or invalidated.
So after knowing all these details in the Transaction Flow, It should be noted that the client application shouldn't wait for the response received by the orderer. Instead it should just request the orderer to deliver the endorsed transactions and the application should be subscribed to the events emitted by the peers so that it should know or be notified that the transactions are actually immutably committed in the channel's chain.
You can have further help regarding event subscription in the Fabric Node SDK docs.
What happen if data is written into the chaincode and after that
orderers deny to write the transaction into the ledger?
This is simply impossible as data is appended to the chain only when the transaction is validated through proper endorsements from the endorser peers (specified by the endorsement policy) and then finally delivered to the committer peers to append the new values in the chain and update the world state. Data is only written in the chain after it passes all the validations and hence an orderer can never deny for the changes made in the data.
I found another reason for this delay.
The batchtimeout variable in configtx.yaml is set to 2 seconds. So it will do its processing and then wait for 2 seconds and then cut a block. So for write operation it takes approximately 2.5 seconds.
Related
I have coded below logic to use fabric network event listener which will listen to transaction commit. However, it is working fine when transaction endorsed successfully but not when transaction endorsed unsuccessfully. Kindly let me know if I am missing something.
Code snapshot:
const transaction = networkObj.contract.createTransaction('addOrg');
const tx_id = transaction.getTransactionID().getTransactionID();
await transaction.addCommitListener((err: any, txId: any, status: any, blockHeight: any) => {
console.log('inside listener');
if (err) {
console.log(err)
return
}
if (status === 'VALID') {
console.log('transaction committed');
console.log('txId: ',txId,'status: ',status,'blockHeight: ',blockHeight);
console.log('transaction committed end');
} else {
console.log('err transaction failed');
console.log(status);
}
});
transaction.submit(OrgAdd.organization, OrgAdd.orgShortName, OrgAdd.orgType, OrgAdd.industryType)
let responseMessage: ResponseMessage = new ResponseMessage({ message: tx_id });
console.log('before return');
return responseMessage;
Logs when transaction is endorsed successfully vs unsuccessfully.
Successful:
Connected to mychannel.
Connected to contract. p2pmembers
Done connecting to network.
OrgAdd: {
organization: 'Manufacturer 10',
orgShortName: 'MF10',
orgType: 'manufacturer',
industryType: 'Electronics'
}
before return
inside listener
transaction committed
<<txId:>> 7b1767397a9821e0e2e0b16c7f7ad4ada9d15a8a7b838c5cc542be50e260d497 <<status:>> VALID <<blockHeight:>> 116
transaction committed end
Unsuccessful
Connected to mychannel.
Connected to contract. p2pmembers
Done connecting to network.
OrgAdd: {
organization: 'Manufacturer 10',
orgShortName: 'MF10',
orgType: 'manufacturer',
industryType: 'Electronics'
}
before return
2020-06-22T11:32:13.973Z - warn: [DiscoveryEndorsementHandler]: _build_endorse_group_member >> G0:0 - endorsement failed - Error: transaction returned with failure: Error: MF10 organization does exist
2020-06-22T11:32:13.975Z - error: [DiscoveryEndorsementHandler]: _endorse - endorsement failed::Error: Endorsement has failed
If endorsement fails (or the send to the orderer fails) then the call to transaction.submit() will throw an error. Note that transaction.submit() is an async function so you should be calling it with await transaction.submit(). Without the await you will have an unhandled promise rejection.
Note too that the call to transaction.submit() returns the response payload from the transaction invocation so, unless your transaction function doesn't return any payload, you should be capturing that response:
const response = await transaction.submit(OrgAdd.organization, OrgAdd.orgShortName, OrgAdd.orgType, OrgAdd.industryType);
Typically you don't need to do your own commit listening as the call to transaction.submit() does this for you. You can change the default behaviour so submit does not wait for commit events, or change the strategy it uses to decide when a commit is successful, but by default it will be doing that commit waiting for you. If the commit fails, transaction.submit() will again throw an error. The error that comes out has proposal responses attached to allow you to investigate the reason for failure.
try {
const response = await transaction.submit(OrgAdd.organization, OrgAdd.orgShortName, OrgAdd.orgType, OrgAdd.industryType);
// Successful transaction endorsement and commit
} catch (error) {
// Failure to endorse or commit
}
Some reasons why you might need to use the kind of pattern you are describing with a separate commit listener are:
You want to process the transaction response immediately after the transaction is sent to the orderer but still check for commit success at the client end.
You want to do checking for commit success entirely asynchronously; perhaps in a different process for performance reasons.
These are both atypical cases and may result in subsequent transactions seeing unexpected state if they are interacting with the same ledger keys as the previous transaction and are processed by peers that have not yet committed that previous transaction. In general, just awaiting the one line call to transaction.submit() and letting it handle waiting commit events for you will give you the best experience.
The Fabric model for transacting follows 'endorse' (or, execute), 'order', 'commit'. A proposal is sent for endorsement at one or more peers, if the endorsements are successful, the client assembles the proposal and endorsements into a transaction which is submitted to ordering. Once the transaction is ordered, the peers receive it in a block and perform validation (ensuring that the correct endorsements are present) and version control checks (ensuring that all of the inputs to execution are unmodified). For a detailed architectural review see this paper.
Event listeners are for 'commit events', which are emitted in that last stage where the peer checks to see if the transaction is valid/consistent. If the initial endorsement fails, then the client never submits the transaction to ordering, and therefore, no commit event occurs.
You may be confusing "endorsement failure" with "endorsement policy failure". An endorsement policy failure occurs when the client does not seek enough endorsements, or endorsements from the right peers, but submits the transaction anyway. You will see an event with a failure in this case, but there was no error at endorsement time.
I am using client.channel.Execute API in fabric-sdk-go to invoke the ledger update Txs in chaincode.
I know my chaincode for ledger update is correct because invoke Tx when run from cli container command line is working perfectly all the times.
Few times, randomly, ledger updates are not reflecting when executed as REST API call from POSTMAN like below. In those cases, response code is 200 with correct response payload suggestive of successful chaincode running.
`
chaincodeID := "hcc"
fcn := "GiftToken"
args := [][]byte{
[]byte(reqBody.TokenID),
[]byte(reqBody.GiftToUserID),
[]byte(GiftTokenCountAsString),
}
setup := lib.GetFabricSetup()
transientDataMap := make(map[string][]byte)
transientDataMap["result"] = []byte("Transient data in GiftToken invoke")
response, err := setup.Client.Execute(channel.Request{ChaincodeID: chaincodeID, Fcn: fcn, Args: args, TransientMap: transientDataMap})
I am running Fabric 1.4.4 images in docker containers. My network has 1 org with 4 peer nodes.
Surely missing some aspect which is leading to this sort of behaviour.
Thanks in advance.
It takes time for all peers to sync their blocks. Once peers receive these blocks they update their world state, so you can see your change via querying.
When you query for the "just executed" transaction, you may hit one of other peers. If you want immediate result, ensure that you're querying the same peer(s) where you actually executed your transaction. You may try putting some delay to see other peers as well get the block.
The reason why you see the change immediately on CLI, is about the way of the client implementation. On CLI command execution, you specify the peer explicitly. So transaction is executed on one peer and queried on the same peer (no issues). You may prove this behavior by immediately querying (via CLI) another organization's peer just after you execute the transaction (via CLI).
However with your client, probably since you don't explicitly specify the peer, your client SDK uses peers' discovery service and find a peer in the network for you and use it.
Due to this reason, when endorsement policy is formed like "AND(org1, org2)", client SDK actually queries 2 peers (one each org) and compare results.
Migration of Ordering service from Kafka to Raft.
As we understand the Ordering service never signs the transaction in Fabric. Dose this migration will effect anything on old transaction ordered by old ordere?
Also when we query transaction(local peer query), why do we set the orderer flag?
As long as you have successfully migrated consensus type from kafka >> raft
You are allowed to proceed with transactions.
Question1: Ordering service never signs the transaction in Fabric
Endorsing peers alone will sign transactions & orderer signs the blocks
Question2: Dose this migration will effect anything on old transaction ordered by old ordere?
No, If migration is successful then you are OK to proceed
If you would have followed this link and complete without errors https://hyperledger-fabric.readthedocs.io/en/release-1.4/kafka_raft_migration.html
THEN OK, dont worry about previous data, All SAFE.
However, let me know if you need any assistance in migration. Feel free to create another question reg: migration.
If you want to see old blocks after migration
check this snippet
// keep the block_reg to unregister with later if needed
block_reg = channel_event_hub.registerBlockEvent((block) => {
console.log('Successfully received the block event');
<do something with the block>
}, (error)=> {
console.log('Failed to receive the block event ::'+error);
<do something with the error>
},
{startBlock:23}
);
startBlock:{can be any Block No}
check > https://fabric-sdk-node.github.io/tutorial-channel-events.html
you will get complete block as json format, you will have orderer signature so that you can check which orderer has sealed this block.
I read below statement from 'Transaction Flow' chapter of hyperledger-fabric docs.
( https://hyperledger-fabric.readthedocs.io/en/latest/txflow.html )
If the client application intends to submit the transaction to Ordering Service to update the ledger, the application determines if the specified endorsement policy has been fulfilled before submitting
Does the 'Endorsement Policy' means endorsement policy for chaincode that can be specified in the CLI?
( c.f. -P "OR ('Org1MSP.member','Org2MSP.member')" )
If so, can I get an example that check 'Endorsement Policy' in node.js application?
Thank you.
Check for consistency of endorsements is already part of the SDK, so it should be transparent for the client code, please take a look on Channel.js:
var endorsements = [];
let proposalResponse = proposalResponses;
if(Array.isArray(proposalResponses)) {
for(let i=0; i<proposalResponses.length; i++) {
// make sure only take the valid responses to set on the consolidated response object
// to use in the transaction object
if (proposalResponses[i].response && proposalResponses[i].response.status === 200) {
proposalResponse = proposalResponses[i];
endorsements.push(proposalResponse.endorsement);
}
}
} else {
if (proposalResponse && proposalResponse.response && proposalResponse.response.status === 200) {
endorsements.push(proposalResponse.endorsement);
}
}
if(endorsements.length < 1) {
logger.error('sendTransaction - no valid endorsements found');
return Promise.reject(new Error('no valid endorsements found'));
}
Now this code checks that it got consistent endorsement response from all endorsement peers request has been sent to.
While it's up to the client application logic to ensure whenever it has satisfiable set of endorsement signatures. E.g. if endorsement policy is
AND(OR(Org1.member, Org3.member), OR(Org2.member, Org3.member))
and you got signatures only of org1 and org2, the above check will pass, while application has to be aware of endorsement rule and understand that responses of org1 and org2 alone is not enough and need to get also endorsement from org3.
With upcoming versions of Fabric, most likely v1.2 there will be service discovery capabilities where application will be provide capabilities to automatically detect satisfiable subsets of endorsing peers, making this part easier for app developer.
JIRA issue: https://jira.hyperledger.org/browse/FAB-7420
I'm trying to set up a network of four organisations, each with one peer (set as admin), and an orderer with kafka ordering service along with a client, which makes 7 VMs.
Setting up the cryptographic material, starting every channel (two of them), joining them with the appropriate peers and installing/instantiating chaincode happens with no trouble.
When trying to invoke chaincode, the chaincode can be successfully invoked (status:200) however the results do not seem to be recorded on the ledger. When running chaincode which returns the value for a/multiple key(s), the resulting payload is simply null. This seems to be due to a fault in the ordering service: I get keeping the error message "Error reading from stream: rpc error: code = Canceled desc = context canceled".
Here are logs for the orderer and endorsing peers of the transaction (which returns status:200)
Orderer: https://gist.github.com/alexanderlhicks/91f32fb5ed2b6d5d232dcdad0572ffee
Peer1 (initiating transaction): https://gist.github.com/alexanderlhicks/a2c7a37d3e692336dbc9a8a9f7fd66ba
Peer2 (has the chaincode installed): https://gist.github.com/alexanderlhicks/75c76b11a26963d7a6962f51295ea816
Peer3 (on channel but does not have that chaincode installed): https://gist.github.com/alexanderlhicks/951757e880d8c2f658d2d188e25b9274
What could be causing the "Error reading from stream" message which seems to be the only error happening? I'm tried playing around with peer and orderer configurations but to no avail. Searching for this error also returns issues in channel creation, or joining channels, which I assume has gone fine since I can invoke chaincode on the channels with my peers.
The txid in the logs is 61f54319421bae9c47fab505e502b932ff6c6b58d7d3795afe1400466e663a5d.
Cheers.