-
Notifications
You must be signed in to change notification settings - Fork 20
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
feat: Add matching sorts #497
Conversation
9d9285f
to
1985538
Compare
97b21b9
to
836ecc7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great stuff @bgins! Left some comments, let me know if you want to chat through any of them
pkg/solver/matcher/matcher.go
Outdated
span.AddEvent("get_deal.done", trace.WithAttributes(attribute.String("deal.id", deal.ID))) | ||
|
||
// Add match decisions for all matching offers | ||
for _, matchingOffer := range matchingResourceOffers { | ||
addDealID := "" | ||
if selectedResourceOffer.ID == matchingOffer.ID { | ||
addDealID = deal.ID | ||
} | ||
|
||
// All match decisions had matching resource offers, set the result to true. | ||
// The match decision has a deal ID if it's resource offer was selected. | ||
span.AddEvent("add_match_decision.start") | ||
_, err := db.AddMatchDecision(matchingOffer.ID, jobOffer.ID, addDealID, true) | ||
if err != nil { | ||
span.SetStatus(codes.Error, "unable to add match decision") | ||
span.RecordError(err) | ||
return nil, err | ||
} | ||
span.AddEvent("add_match_decision.done") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe have this as it's own private function to shrink the size of the main function and help readability?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also inside the loop we want to have a log when a match is made (though this might flood the log file) or have a log at the end of the loop to signify how many matches were made
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored this loop into a addMatchDecisions
helper function: 5e99321
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And added the logging here: 2a57b7b
c0ce9b4
to
c56ccfb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Was able to run a cow-say locally and thank you for writing up those issues for tracking 🚀
We will re-use these for benchmarks in a new test file.
Our logMatch function expects a match result value. In addition, there's no advantage to using references here.
We will use these helpers for top-level solver integration tests in addition to the store integration tests.
We integrate at the solver to test matcher and store functionality together.
2a57b7b
to
ccd3946
Compare
Summary
This pull request makes the following changes:
GetJobOffers
store method with oldest first sort queryGetResourceOffers
store method with oldest first queryisCheaperOrOlder
match helperTestIsCheaperOrOlder
unit testmatchOffers
benchmarkbench
GitHub actionIn addition, it makes supporting and refactoring changes:
matcher_test.go
test cases topkg/solver/matcher/testing.go
for reuse by the benchmarkstore_test.go
store helpers topkg/solver/testing.go
for reuse by the matching and sorting integration testmatchResult
(fix forlogMatch
logger)This PR updates the matching algorithm with pricing and age sorting preferences. In addition, we add new test coverage, including relative benchmarks to report performance and alert on performance degradation.
Task/Issue reference
Closes: #492
Test plan
Unit tests
We have added a
TestIsCheaperOrOlder
unit test to test ourisCheaperOrOlder
match helper. The test runs with the other unit tests or alone with:Integration tests
We have extended our store integration tests to cover our get oldest offer queries. Run the store integration tests:
The new tests are
TestJobOfferSort
andTestResourceOfferSort
.We have added a new matching and sorting integration test to the solver. Run the test with:
Run all of the integration tests with:
Benchmarks
Our first benchmark has arrived! We have added documentation describing the benchmark in the local development guide and a note celebrating them in our README. 🎉
The benchmark measures the performance of
matchOffers
. Run it with:Our primary goal is relative benchmarking in CI. Aside from running in a isolated GitHub action, we make no attempts to control the environment where the benchmark runs. We want to know if a change has degraded performance, but we are not too concerned with accuracy.
The new
bench
GitHub action runs the benchmark. We get some nice reporting and alerting frombenchmark-action/github-action-benchmark
:bench
action. For example: https://github.com/Lilypad-Tech/lilypad/actions/runs/13023788174/attempts/1#summary-36329383772In addition, when we push to
main
, the action publishes the benchmark results to a historical chart: https://lilypad-tech.github.io/lilypad/dev/bench/ (some sample data from commits on this PR, can clean up later)Details
The pricing and age sorting preferences use the following criteria:
In practice, we request job offers and resource offers from the database sorted oldest first. For each job offer, we build a list of matching resource offers. We apply the pricing preference over the matched resource offers:
We may be able to optimize in the future by retrieving resource offers cheapest first from the database, but we believe this to be a good starting point with room to grow. 😄
Establishing offer age
When we query the database store, we sort oldest first relying on the database engine to sort for us. We don't have this option for the memory store, so we use the job offer and resource offer
CreatedAt
field.Note that this implementation likely makes the memory store unsuitable for production use cases. A job creator or resource provider could forge timestamps to game the system.
Related issues or PRs
Epic: https://github.com/Lilypad-Tech/internal/issues/367