Transaction api returns error when blockhash is empty
zsluedem opened this issue · comments
What did you expect to see?
404 or 502 in https://observer-exch2.services.mainnet.rchain.coop/api/transactions/
What did you see instead?
2022-06-16 04:56:22,307 [ERROR] [node-runner-27 ] [Routes$ResponseErrorHandler$1]- HTTP API response error
org.lmdbjava.Dbi$BadValueSizeException: Unsupported size of key/DB name/data, or wrong DUPFIXED size (-30781)
at org.lmdbjava.ResultCodeMapper.checkRc(ResultCodeMapper.java:72)
at org.lmdbjava.Dbi.get(Dbi.java:239)
at coop.rchain.store.LmdbKeyValueStore.$anonfun$get$2(LmdbKeyValueStore.scala:62)
at runToFuture @ coop.rchain.catscontrib.TaskContrib$TaskOps$.unsafeRunSync$extension(taskOps.scala:27)
@zsluedem Please add more info for @hilltracer how to fix this bug.
We also need to log API errors as warnings which will indicate that error is not related to internal node error and node can continue running.
@tgrospic, @zsluedem, I investigated the problem. The exception Dbi.BadValueSizeException
occurs in the code
when we are passing empty hash as a key.
This is implementation of Dbi.get
:
public T get(final Txn<T> txn, final T key) {
if (SHOULD_CHECK) {
requireNonNull(txn);
requireNonNull(key);
txn.checkReady();
}
txn.kv().keyIn(key);
final int rc = LIB.mdb_get(txn.pointer(), ptr, txn.kv().pointerKey(), txn
.kv().pointerVal());
if (rc == MDB_NOTFOUND) {
return null;
}
checkRc(rc);
return txn.kv().valOut(); // marked as out in LMDB C docs
}
As you can see in some case (rc == MDB_NOTFOUND
) it returns null
. I tried to handle Dbi.BadValueSizeException
by changing code to
keys.map(x => Try(dbi.get(txn, x)).collect { case null => throw new Exception("Not found") }.toOption.map(fromBuffer))
(note that Try(null).toOption
gives Some(null)
instead of None
)
but it has different behavior and kills node after launch.
So problem simply can be fixed by checking hash is not empty:
(if (blockHash.isEmpty) None.pure[F] else store.get1(blockHash)) >>= { transactionOpt => ...
And the response is "requirement failed: Expected 32 but got 0"
which is more clear for users.
What do you think? Is it proper solution or I should investigate why node dies with using Try
?
Upd
I was not correct. LmdbKeyValueStore.get
should be implemented as
keys.map(
x =>
Try(dbi.get(txn, x))
.collect { case null => throw new Exception("Not found"); case b: ByteBuffer => b }
.toOption
.map(fromBuffer)
)
In that case user will see response "requirement failed: Expected 32 but got 0"
also but this fix of LmdbKeyValueStore.get
better because this method can be used with incorrect arguments in other places.
@stanislavlyalin nice work.