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

Replace Hangfire with a simpler queue service #407

Closed
Tracked by #356
mscottford opened this issue Nov 14, 2022 · 0 comments · Fixed by #417
Closed
Tracked by #356

Replace Hangfire with a simpler queue service #407

mscottford opened this issue Nov 14, 2022 · 0 comments · Fixed by #417
Labels
enhancement New feature or request
Milestone

Comments

@mscottford
Copy link
Member

mscottford commented Nov 14, 2022

We're currently using Hangfire for processing IApplicationEvent and IApplicationActivity instances. This is a great library that provides a lot of functionality, and it was decently easy for us to get started with it. It does come with some downsides.

  • Serialization overhead: all work that's stored in the job queue has to be serialized as it is being stored in the queue and deserialized as it is read out of the queue. This is great for applications that need to persist their work in a shared storage mechanism so that it can be read by another process. However, in our case, all of our work happens in the same process, so it would make more sense to keep all of our work in memory instead. If we could do that, we'd improve performance by not having to serialize and deserialize everything going into and coming out of the work queue. Hangfire also requires us to depend on the Newtonsoft JSON library which is not as performant as System.Text.Json.
  • Complicated setup: Because of the way that Hangfire is designed, we had to extract (read: copy and paste) a lot of ASP.NET specific setup code and port it to work without ASP.NET. This makes the Hangfire a rather brittle dependency.
  • Synchronous work: Hangfire does not provide a way for us to add asynchronous methods to the work queue. This means that we're not able to take advantage of async and await to better mange CPU resources. As a result, a lot of our CPU usage percentage is caused by us just waiting on IO operations to complete. It would be much more performant to be able to leverage asnyc method calls for those cases.
  • Work stored as expressions: Because the work that goes in the queue has to be represented by normal expressions, we've had to implement some strange work arounds that will break in unobvious ways if someone refactors the wrong method.

A solution that's worth investigating is building our own queue system using an unbounded System.Threading.Channel. to store the work that needs to be completed. The work can be stored simple Task<T> objects. Microsoft has a really decent guide that we can use as a starting point for this effort. We'd then be able to convert our IApplicationActivity and IApplicationEvent classes to provide async capable methods so that we can obtain further performance improvements.

@mscottford mscottford moved this from Backlog to In Progress in Freshli Nov 15, 2022
mscottford added a commit that referenced this issue Nov 22, 2022
Removes dependency on Hangfire. There's nothing wrong with Hangfire, we
weren't taking advantage of most of its features. It also required
serializing objects, and we were able to simplify our implementations by
not having to worry about serialization of items that go into the work
queue.

This change also introduces `async`/`await` operators to much of the
codebase.

While I was hoping for dramatic performance improvements, it only
appears to have shaved off about 2 seconds of runtime for the continuous
integration process.

Another benefit is that we were able to remove a lot of Hangfire
configuration code. That code was rather brittle because it was
copy-and-pasted out of the Hangfire repository. Upgrading to future
versions of Hangfire may have been a challenge. Now we don't need to
stress out about that issue.

Fixes #407.
Repository owner moved this from In Progress to Done in Freshli Nov 22, 2022
mscottford added a commit that referenced this issue Jun 2, 2023
Removes dependency on Hangfire. There's nothing wrong with Hangfire, we
weren't taking advantage of most of its features. It also required
serializing objects, and we were able to simplify our implementations by
not having to worry about serialization of items that go into the work
queue.

This change also introduces `async`/`await` operators to much of the
codebase.

While I was hoping for dramatic performance improvements, it only
appears to have shaved off about 2 seconds of runtime for the continuous
integration process.

Another benefit is that we were able to remove a lot of Hangfire
configuration code. That code was rather brittle because it was
copy-and-pasted out of the Hangfire repository. Upgrading to future
versions of Hangfire may have been a challenge. Now we don't need to
stress out about that issue.

Fixes #407.
@mscottford mscottford added this to the v0.6.0 milestone Aug 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

1 participant