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

feat: Add matching sorts #497

Merged
merged 18 commits into from
Jan 31, 2025
Merged

feat: Add matching sorts #497

merged 18 commits into from
Jan 31, 2025

Conversation

bgins
Copy link
Contributor

@bgins bgins commented Jan 23, 2025

Summary

This pull request makes the following changes:

  • Update GetJobOffers store method with oldest first sort query
  • Update GetResourceOffers store method with oldest first query
  • Add pricing and age sorting preferences to matching algorithm
    • Update matcher to retrieve offers oldest first from store
    • Add isCheaperOrOlder match helper
      • Add TestIsCheaperOrOlder unit test
    • Add matching and sorting integration test
  • Add matchOffers benchmark
    • Add bench GitHub action
    • Document benchmarks in README and local development guide

In addition, it makes supporting and refactoring changes:

  • Pass solver service logger to matcher
  • Move matcher_test.go test cases to pkg/solver/matcher/testing.go for reuse by the benchmark
  • Move store_test.go store helpers to pkg/solver/testing.go for reuse by the matching and sorting integration test
  • Return match result values instead of refs from matchResult (fix for logMatch 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 our isCheaperOrOlder match helper. The test runs with the other unit tests or alone with:

go test -tags="unit" -v -count 1 -run TestIsCheaperOrOlder ./.../matcher

Integration tests

We have extended our store integration tests to cover our get oldest offer queries. Run the store integration tests:

go test -tags="integration,solver" ./.../store -v -count 1

The new tests are TestJobOfferSort and TestResourceOfferSort.

We have added a new matching and sorting integration test to the solver. Run the test with:

go test -tags="integration,solver" ./.../solver -v -count 1

Run all of the integration tests with:

./stack integration-tests-solver

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:

./stack benchmarks-solver

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 from benchmark-action/github-action-benchmark:

In 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:

  • When considering a job offer in fixed pricing mode, sort resource offers by oldest offer first
  • When considering a job offer in market pricing mode, sort resource offers by cheapest offer first
    • If multiple resource offers offer the same cheap price, we sort the resource offers again oldest first

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:

  • If the job offer uses fixed pricing, select the first (oldest) resource offer that meets the price requirement
  • If the job offer uses market pricing, select the cheapest offer and break ties with age

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

@cla-bot cla-bot bot added the cla-signed label Jan 23, 2025
@bgins bgins force-pushed the bgins/feat-add-matching-sorts branch 16 times, most recently from 9d9285f to 1985538 Compare January 24, 2025 17:53
@bgins bgins self-assigned this Jan 25, 2025
@bgins bgins force-pushed the bgins/feat-add-matching-sorts branch 3 times, most recently from 97b21b9 to 836ecc7 Compare January 29, 2025 03:00
@bgins bgins marked this pull request as ready for review January 29, 2025 17:25
@bgins bgins requested a review from a team as a code owner January 29, 2025 17:25
@bgins bgins requested a review from narbs91 January 29, 2025 17:25
Copy link
Collaborator

@narbs91 narbs91 left a 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/match.go Show resolved Hide resolved
pkg/solver/matcher/match.go Show resolved Hide resolved
pkg/solver/matcher/matcher.go Show resolved Hide resolved
pkg/solver/matcher/matcher.go Show resolved Hide resolved
pkg/solver/matcher/matcher.go Show resolved Hide resolved
pkg/solver/matcher/matcher.go Outdated Show resolved Hide resolved
Comment on lines 173 to 191
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")
Copy link
Collaborator

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?

Copy link
Collaborator

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

Copy link
Contributor Author

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

Copy link
Contributor Author

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

pkg/solver/matcher/matcher.go Show resolved Hide resolved
pkg/solver/matcher/matcher.go Outdated Show resolved Hide resolved
@bgins bgins force-pushed the bgins/feat-add-matching-sorts branch from c0ce9b4 to c56ccfb Compare January 30, 2025 18:18
Copy link
Collaborator

@narbs91 narbs91 left a 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.
@bgins bgins force-pushed the bgins/feat-add-matching-sorts branch from 2a57b7b to ccd3946 Compare January 31, 2025 20:29
@bgins bgins merged commit c310839 into main Jan 31, 2025
5 checks passed
@bgins bgins deleted the bgins/feat-add-matching-sorts branch January 31, 2025 20:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Adding sorting to solver matching algorithm
2 participants