Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Benchmark Test (BenchmarkReadMessage/Funding_Locked) in the lnwire package #7356

Merged

Conversation

ziggie1984
Copy link
Collaborator

@ziggie1984 ziggie1984 commented Jan 24, 2023

Change Description

This PR fixes #7152

The benchmark tests failed as described in the issue. Currently the tests produce random data for the ExtraOpaqueData field of most of the message types. This can be dangerous as soon as new data is added to a msg type via a tlv record. In this case other tests will fail too, if they use the random data generator for the tlv part.
The benchmark tests failed now, because the funding_locked msg type is the first message in the code base which has an tlv record nested in the overall message type therefore this behaviour was not observed before. Except the Open_channel msg but this is a special kind of message because the upfront_script has to be always in the tlv_data therefore the tests swap the ExtraOpaqueData and encode the relevant upfront_script record in the tlv stream

My approach was to narrow down the funding_locked message, discarding the random part of the ExtraOpaqueData data and including the new AliasScid tlv record, this way the tests will test the encode and decode method of the funding_locked msg more throughly.

Though as soon as another msg type introduces a new nested tlv field the tests will fail again. So maybe we should get rid of the random message generation part for the ExtraOpaqueData field for all msg types, or restrict its size to 0, and add additional tests which really try to push the max size of the whole message to its boundaries to actually test the size limits of the payload etc.

Moreover I added the Benchmark tests to the CI which which only runs the benchmark tests, explicitly avoiding all the other unit tests with the test.run=NONE option.

Steps to Test

In the lnwire directory run: go test -v -bench=BenchmarkReadMessage

or more general:

make unit-bench which runs all benchmark tests of all packages

Pull Request Checklist

Testing

  • Your PR passes all CI checks.
  • Tests covering the positive and negative (error paths) are included.
  • Bug fixes contain tests triggering the bug to prevent regressions.

Code Style and Documentation

📝 Please see our Contribution Guidelines for further guidance.

@ziggie1984 ziggie1984 changed the title benchmarking fundinglocked Fix Benchmark Test (BenchmarkReadMessage/Funding_Locked) in the lnwire package Jan 24, 2023
@ziggie1984 ziggie1984 force-pushed the benchmarking-fundinglocked branch 3 times, most recently from d46da4c to b38fb2d Compare January 24, 2023 21:26
@morehouse
Copy link
Collaborator

The benchmark tests failed now, because the funding_locked msg type is the first message in the code base which has an tlv record nested in the overall message type therefore this behaviour was not observed before.

Don't we do something similar for UpfrontShutdownScript in OpenChannel and AcceptChannel? Why do the benchmarks pass for them?

Moreover should we also include the Benchmark tests in the overall testrun sothat we capture future failure more reliably?

Yes, I think this is the best way to avoid bit rot. Then when a new TLV is introduced, the test will fail and alert us that we need to update the benchmarks.

@ziggie1984
Copy link
Collaborator Author

ziggie1984 commented Jan 26, 2023

Thanks for your review, appreciate it :)

Good point, thats because the upfrontscript needs to be in the tlv-data, thats a hard requirement according to the bolts:

if both nodes advertised the option_upfront_shutdown_script feature:
MUST include upfront_shutdown_script with either a valid shutdown_scriptpubkey as required by shutdown scriptpubkey, or a zero-length shutdown_scriptpubkey (ie. 0x0000).

https://github.com/lightning/bolts/blob/master/02-peer-protocol.md

therefore the compelete random string of ExtraOpaqueData gets swapped for the new 0x0000 byte tlv record. Thats why when reading this tlv record everything is fine and no random data is in the Opaque field. For the funding_locked there is no mandatory field in the tlv record therefore the random stream is written to the ExtraOpaqueData

The swapping happens here

@ziggie1984
Copy link
Collaborator Author

ziggie1984 commented Jan 26, 2023

Because its a benchmark test, maybe its better to write a function which fills the ExtraOpaqueData data with valid tlv records (sorted) and also consider each msg for itself, adding as much data as possible until max data is reached for every message?

Otherwise just passing almost empty ExtraOpaqueData to the parser, as now done by the funding_locked msg (the current change) is not really worth it for a benchmark

@ziggie1984 ziggie1984 force-pushed the benchmarking-fundinglocked branch 2 times, most recently from 3d816d5 to fdea26a Compare January 26, 2023 19:12
@ziggie1984 ziggie1984 force-pushed the benchmarking-fundinglocked branch from fdea26a to 41dc347 Compare February 3, 2023 17:57
@ziggie1984 ziggie1984 marked this pull request as ready for review February 3, 2023 18:28
Copy link
Collaborator

@morehouse morehouse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix!

One suggestion below on simplifying the new code.

Comment on lines 456 to 467
msg := lnwire.NewFundingLocked(lnwire.ChannelID(c), pubKey)
msg.ExtraData = createExtraData(t, r)

// When testing the WriteMessage function we need to populate
// the alias to test the encoding of the tlv_stream in the
// funding_locked message (now called channel_ready in the bolt spec).
_, err = r.Read(c[:8])
require.NoError(t, err, "unable to generate channel alias")
aliasScid := lnwire.NewShortChanIDFromInt(
binary.BigEndian.Uint64(c[:8]),
)

msg.AliasScid = &aliasScid
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could simplify this code a bit. Something like:

msg := lnwire.FundingLocked{
  ChanID: lnwire.ChannelID(c),
  NextPerCommitmentPoint: pubkey,
  AliasScid: lnwire.NewShortChanIDFromInt(r.Int63()),
  ExtraData: make([]byte, 0),
}

The RecordProducer stuff below to populate ExtraData could stay the same.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, Though I had to keep the aliasScid separate because the AliasScid is a pointer but the NewShortChanIDFromInt returns a value type

@ziggie1984 ziggie1984 force-pushed the benchmarking-fundinglocked branch from 41dc347 to 3025541 Compare February 3, 2023 22:46
Copy link
Collaborator

@morehouse morehouse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment on lines 471 to 473
err = lnwire.EncodeMessageExtraData(&msg.ExtraData, recordProducers...)
require.NoError(t, err, "unable to encode channel alias into "+
"tlv stream")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, we don't need to call EncodeMessageExtraData here, since it will also be done inside Encode.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks you are right, its now part because we write it before 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though I think also including it in the extradata is the correct way of doing it, because normally when calling newMsgFundingLocked I would expect a correctly filled message of this type without having to know that I need to encode it first so the TLV data is included in the right place ?

Copy link
Collaborator Author

@ziggie1984 ziggie1984 Feb 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but I am good with both ways, I deleted it but added a remark why we do not write the TLV data in the ExtraData in the method. I think that's the best way.

@@ -101,12 +101,14 @@ ifeq ($(UNIT_TARGETED), yes)
UNIT := $(GOTEST) -tags="$(DEV_TAGS) $(LOG_TAGS)" $(TEST_FLAGS) $(UNITPKG)
UNIT_DEBUG := $(GOTEST) -v -tags="$(DEV_TAGS) $(LOG_TAGS)" $(TEST_FLAGS) $(UNITPKG)
UNIT_RACE := $(GOTEST) -tags="$(DEV_TAGS) $(LOG_TAGS) lowscrypt" $(TEST_FLAGS) -race $(UNITPKG)
UNIT_BENCH := $(GOTEST) -tags="$(DEV_TAGS) $(LOG_TAGS)" -test.bench=. -test.run=XXX $(UNITPKG)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the test.run=XXX here? Is the intent to only run very short benchmark tests to make sure they work in general but finish quickly?

Copy link
Collaborator Author

@ziggie1984 ziggie1984 Feb 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intention with the XXX regex was to only run the bench-mark tests without any other tests, because otherwise it would run the normal unit-tests too. And this make commd should only handle the Benchmark Tests ?

I will add a comment clarifying it, and also changed the regex to something better with NONE which is a special value in golang and matches no tests

@ziggie1984
Copy link
Collaborator Author

Hmm I understand your points regarding adding it to the CI tests, so the initial issue of this PR was, because it was not on anyone's radar that the Benchmark tests were failing. But adding them to the CI is also maybe not the right approach. Maybe only execute them for specific releases so that we make sure they do not fail and pass at least ?

@ziggie1984 ziggie1984 force-pushed the benchmarking-fundinglocked branch from 3025541 to f95cac7 Compare February 7, 2023 22:39
@guggero
Copy link
Collaborator

guggero commented Feb 8, 2023

Hmm I understand your points regarding adding it to the CI tests, so the initial issue of this PR was, because it was not on anyone's radar that the Benchmark tests were failing. But adding them to the CI is also maybe not the right approach. Maybe only execute them for specific releases so that we make sure they do not fail and pass at least ?

Perhaps we can just run the benchmark tests on master branch? So they would be run after each merged PR but at least not for each update of each PR, which already saves some cycles but would still be noticed when they break (when we actually have fully green CI which is hopefully very soon).
What I mean is: if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} in the unit-bench job.

@ziggie1984
Copy link
Collaborator Author

I like your idea so I changed the CI workflow for Benchmark tests.

@ziggie1984 ziggie1984 force-pushed the benchmarking-fundinglocked branch from f95cac7 to 663c90b Compare February 8, 2023 10:39
Copy link
Member

@yyforyongyu yyforyongyu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! I like that we are considering putting benchmarks into our CI, but the current approach may not add many benefits.

At the moment, for a particular PR, we run benchmarks twice, one before and one after, to assess the performance improvement brought by the PR. Running benchmarks alone, however, doesn't provide us with any useful info.

I thought about adding a new workflow here to run benchmarks periodically. Or run them per release to see the performance change between releases, or run them for particular PRs, with the help of a github bot to report the results.

So I'd say we should remove it from CI because it doesn't give us useful info. And similar bugs can get fixed whenever the benchmarks are run. Not ideal, until we can properly figure out how to run them automatically.

@ziggie1984
Copy link
Collaborator Author

At the moment, for a particular PR, we run benchmarks twice, one before and one after, to assess the performance improvement brought by the PR. Running benchmarks alone, however, doesn't provide us with any useful info.

What do you mean by "run twice" here, are the benchmarks already included in some kind of test-script or "should" a developer run the benchmark tests twice?

Anyways I agree maybe just put them out of the CI for now as you said and look in a separate PR how we can integrate the benchmarks properly.

@ziggie1984 ziggie1984 force-pushed the benchmarking-fundinglocked branch from 663c90b to ad1bc03 Compare February 23, 2023 15:22
Copy link
Member

@yyforyongyu yyforyongyu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM👍

What do you mean by "run twice" here, are the benchmarks already included in some kind of test-script or "should" a developer run the benchmark tests twice?

I meant the developer should run it twice to understand the performance change. An example would be #4884, which used the bench results to guide the code change.

@ziggie1984 ziggie1984 force-pushed the benchmarking-fundinglocked branch from ad1bc03 to d3ced08 Compare February 24, 2023 06:57
@lightninglabs-deploy
Copy link

@ziggie1984, remember to re-request review from reviewers when ready

@yyforyongyu
Copy link
Member

Maybe this can be rebased and merged🧐

The addition of AliasScid in the tlv_stream field of the
funding_locked msg made the requirement for testdata of the
ExtraOpaqueData part for the funding_locked msg type more strict.
Now the input testdata for the funding_locked test is more specific
and includes the new added AliasScid tlv record.
ziggie1984 added 2 commits May 9, 2023 16:47
Add benchmark tests as a separate test run besides the current
unit tests.
@ziggie1984 ziggie1984 force-pushed the benchmarking-fundinglocked branch from d3ced08 to 0c31eb7 Compare May 9, 2023 14:59
@ziggie1984
Copy link
Collaborator Author

Maybe this can be rebased and merged🧐

Rebased and ready to be merged I think.

Copy link
Collaborator

@guggero guggero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🎉

@guggero guggero merged commit ae30581 into lightningnetwork:master May 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BenchmarkReadMessage/FundingLocked failure
5 participants