Git Product home page Git Product logo

topshelf.leader's Introduction

Topshelf.Leader Build Status

An extension method to the Topshelf ServiceConfigurator<T> class that adds Leader checking to the service startup.

When would I use this?

Use it when your services require active / passive or any form of high availablility where the services aren't able to naturally compete.

  • Catch up suscriptions that don't allow competing consumers
  • Services that can operate in an Active / Passive configuration

When should I not use this?

Don't use this extension if you want a non-leader service to perform tasks whilst the leader service is also performing tasks. The design of the extension is that only one service is actively doing anything at any one time.

Getting started

Install-Package Topshelf.Leader

Once the package is installed, create a Console application and wireup the Topshelf service as you normally would except for the WhenStarted() method - this should no longer be used.

You should use the WhenStartedAsLeader() method that Topshelf.Leader provides instead which constains its own version of the WhenStarted() method, one with cancellation token support.

Example

using Topshelf.Leader;

public class Program
{
    static void Main(string[] args)
    {
      var rc = HostFactory.Run(x =>
      {
          x.Service<TheService>(s =>
          {
              s.WhenStartedAsLeader(builder =>
              {
                  builder.WhenStarted(async (service, token) =>
                  {
                      await service.Start(token);
                  });

                  builder.WithHeartBeat(
                      TimeSpan.FromSeconds(30),
                      (isLeader, token) =>
                      {
                          // Track metrics here
                          return Task.CompletedTask;
                      });
              });
              s.ConstructUsing(name => new TheService());
              s.WhenStopped(service => service.Stop());
          });
      });
  }
}

How does it work?

The WhenStarted() method will be executed when the service discovers that it is the current leader. If that situation changes the cancellation token will be set to cancelled. You decide how to handle this situation, throw an exception, exit gracefully or even carry on whatever you were doing - that's entirely up to you.

Example of a service which supports leadership change

    public class TestService
    {
        public async Task Start(CancellationToken stopToken)
        {
            while (!stopToken.IsCancellationRequested)
            {
                // do your work here, if it's async pass the stopToken to it
            }
        }

	public void Stop()
        {
            // Tidy up unmanaged resources
        }
    }

Lease Manager

The responsibility for deciding if your service is the leader is delegated to any class which implements the ILeaseManager interface. The process is as follows:

  1. The process will call ILeaseManager.AcquireLease() until we have obtained a lease (which means that we are the leader)
  2. If we are the leader, the delegate passed to the WhenStarted() builder method is run.
  3. Whilst we are running the ILeaseManager.RenewLease() method is called.
  4. When asked to stop the service we call ILeaseManager.ReleaseLease()

You configure which manager to use during the configuration stage. If one isn't supplied then an in memory manager is used. The in memory manager is not muti-process aware so it is not suitable for production use.

Consider using the Topshelf.Leader.AzureBlob lease manager for production environments. It is backed by Azure Blob Storage and is perfectly suited to production loads. If that doesn't meet your requirements it's easy to write your own.

Configuring the lease manager

var rc = HostFactory.Run(x =>
{
    x.Service<TheService>(s =>
    {
        s.WhenStartedAsLeader(builder =>
        {
            builder.WhenStarted(async (service, token) =>
            {
                await service.Start(token);
            });

            builder.Lease(lcb =>
            {
                lcb.RenewLeaseEvery(TimeSpan.FromSeconds(2))
					.AquireLeaseEvery(TimeSpan.FromSeconds(5))
					.LeaseLength(TimeSpan.FromDays(1))
					.WithLeaseManager(new YourManagerHere());
            });
        });
        s.ConstructUsing(name => new TheService());
        s.WhenStopped(service => service.Stop());
    });
}

Example of a simple lease manager

public class InMemoryLeaseManager : ILeaseManager
{
    private string owningNodeId;

    public InMemoryLeaseManager(string owningNodeId)
    {
        this.owningNodeId = owningNodeId;
    }

    public void AssignLeader(string newLeaderId)
    {
        this.owningNodeId = newLeaderId;
    }

    public Task<bool> AcquireLease(LeaseOptions options, CancellationToken token)
    {
        return Task.FromResult(options.NodeId == owningNodeId);
    }

    public Task<bool> RenewLease(LeaseOptions options, CancellationToken token)
    {
        return Task.FromResult(options.NodeId == owningNodeId);
    }

    public Task ReleaseLease(LeaseReleaseOptions options)
    {
	owningNodeId = string.Empty;
        return Task.FromResult(true);
    }
}

topshelf.leader's People

Contributors

dungimon avatar gitricharde avatar stevewgh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

topshelf.leader's Issues

Quicker lease releasing

When a leader node shuts down, we should be able to release any leases so that other nodes can take over as quickly as possible.

Unhandled Exceptions aren't bubbled to the Topshelf OnException handler.

Any unhandled exceptions thrown by the service should bubble up to the OnException handler.

var rc = HostFactory.Run(x =>
{
	x.Service<Service>(s =>
	{
		s.ConstructUsing(name => new Service());
		s.WhenStartedAsLeader(
			builder =>
			{
				builder.WhenStarted(async (s, token) =>
				{
					await s.StartAsync(token);  // exceptions thrown here ....
				});
			});
	});

	x.OnException(e => { logger.LogException(e); }); // should bubble here
});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.