Thank you for your interest in contributing to MultiLock! This document provides guidelines and instructions for contributing to the project.
- Code of Conduct
- Getting Started
- Development Setup
- How to Contribute
- Coding Standards
- Testing Guidelines
- Pull Request Process
- Commit Message Guidelines
- Provider Development
- Documentation
- Community
This project adheres to the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code. Please read our Code of Conduct for details. Please report unacceptable behavior to stein.gran@protonmail.com.
- Fork the repository on GitHub
- Clone your fork locally:
git clone https://github.com/YOUR-USERNAME/MultiLock.git cd MultiLock - Add the upstream repository:
git remote add upstream https://github.com/steingran/MultiLock.git
- Create a branch for your changes:
git checkout -b feature/your-feature-name
- .NET 8.0 SDK or later
- Docker Desktop (for integration tests)
- Git
- Visual Studio 2022, JetBrains Rider, or VS Code (recommended)
# Restore dependencies
dotnet restore
# Build the solution
dotnet build
# Run unit tests
dotnet test tests/MultiLock.Tests/Integration tests require Docker to be running:
# Start all test services (PostgreSQL, Redis, SQL Server, Consul, ZooKeeper, Azurite)
cd tests
docker-compose up -d
# Wait for services to be healthy
docker-compose ps
# Run integration tests
dotnet test MultiLock.IntegrationTests/
# Stop services when done
docker-compose downMultiLock/
├── src/
│ ├── MultiLock/ # Core framework
│ └── Providers/ # Provider implementations
│ ├── MultiLock.AzureBlobStorage/
│ ├── MultiLock.Consul/
│ ├── MultiLock.FileSystem/
│ ├── MultiLock.InMemory/
│ ├── MultiLock.PostgreSQL/
│ ├── MultiLock.Redis/
│ ├── MultiLock.SqlServer/
│ └── MultiLock.ZooKeeper/
├── tests/
│ ├── MultiLock.Tests/ # Unit tests
│ └── MultiLock.IntegrationTests/ # Integration tests
├── samples/
│ ├── MultiLock.Sample/ # Basic sample
│ └── LeaderElection.MultiProvider/ # Multi-provider demo
└── docs/ # Documentation
- Check existing issues to avoid duplicates
- Provide detailed information when creating a new issue
- Include:
- Clear description of the problem
- Steps to reproduce
- Expected vs actual behavior
- Environment details (.NET version, OS, provider used)
- Stack traces or error messages
- Check existing issues and discussions
- Clearly describe your feature request
- Describe:
- The problem you're trying to solve
- Your proposed solution
- Alternative solutions considered
- Any breaking changes
- Find or create an issue describing what you'll work on
- Comment on the issue to let others know you're working on it
- Follow the coding standards (see below)
- Write tests for your changes
- Update documentation as needed
- Submit a pull request
This project follows modern C# best practices and conventions:
- Use the latest C# syntax available in .NET 8.0
- Enable nullable reference types (
<Nullable>enable</Nullable>) - One class/interface/record per file
- No
#regiondirectives - use comments instead - No brackets for one-line if statements:
// Good if (condition) DoSomething(); // Avoid if (condition) { DoSomething(); }
The project includes an .editorconfig file that enforces consistent formatting. Your IDE should automatically apply these settings.
Key conventions:
- Indentation: 4 spaces for C#, 2 spaces for XML/JSON/YAML
- Line endings: LF (Unix-style)
- Encoding: UTF-8
- Braces: Allman style (new line)
- Naming: PascalCase for public members, camelCase for private fields
- Follow SOLID principles
- Keep methods focused and small
- Use meaningful names for variables, methods, and classes
- Add XML documentation for public APIs
- Handle exceptions appropriately
- Use async/await for I/O operations
- Dispose resources properly (use
usingstatements)
- Use xUnit as the testing framework
- Use Shouldly for assertions (not FluentAssertions)
- Use Moq for mocking
- Follow AAA pattern: Arrange, Act, Assert
- Name tests descriptively:
MethodName_Scenario_ExpectedBehavior
Example:
[Fact]
public async Task TryAcquireLeadershipAsync_WhenNotCurrentlyLeader_ShouldAcquireSuccessfully()
{
// Arrange
var provider = new InMemoryLeaderElectionProvider();
// Act
var result = await provider.TryAcquireLeadershipAsync("group1", "participant1", new Dictionary<string, string>(), TimeSpan.FromMinutes(5));
// Assert
result.ShouldBeTrue();
}- Test against real services (PostgreSQL, Redis, etc.)
- Do NOT use EF Core In-Memory database - use the PostgreSQL instance from docker-compose.yml
- Clean up resources after tests
- Use unique identifiers to avoid test interference
- Mark with
[Trait("Category", "Integration")]
- Aim for high coverage of core functionality
- Test edge cases and error conditions
- Test concurrent scenarios for thread safety
-
Ensure all tests pass:
dotnet test -
Build succeeds without warnings:
dotnet build
-
Update documentation if needed
-
Add/update tests for your changes
-
Follow commit message guidelines (see below)
- Use the PR template provided
- Link related issues using keywords (e.g., "Fixes #123")
- Add appropriate labels:
- Type:
feature,bug,enhancement,docs, etc. - Version impact:
major,minor,patch - Provider-specific:
provider/postgresql,provider/redis, etc.
- Type:
- Keep PRs focused - one feature/fix per PR
- Respond to review feedback promptly
- Ensure CI passes before requesting review
Use conventional commit format:
<type>(<scope>): <description>
Examples:
feat(postgresql): add connection pooling support
fix(redis): resolve race condition in leader election
docs: update contribution guidelines
test(sqlserver): add integration tests for failover
Follow the Conventional Commits specification:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
- feat: New feature
- fix: Bug fix
- docs: Documentation changes
- test: Adding or updating tests
- refactor: Code refactoring
- perf: Performance improvements
- chore: Maintenance tasks
- ci: CI/CD changes
feat(redis): add support for Redis Sentinel
Implements leader election using Redis Sentinel for high availability.
Includes automatic failover detection and reconnection logic.
Closes #45
fix(postgresql): prevent connection leak in heartbeat
The heartbeat mechanism was not properly disposing connections,
leading to connection pool exhaustion under high load.
Fixes #78
If you're adding a new provider:
- Create a new project under
src/Providers/MultiLock.YourProvider/ - Implement
ILeaderElectionProviderinterface - Add extension methods for service registration
- Include the core framework as a dependency
- Write comprehensive tests
- Add documentation and examples
- Update the main README to list the new provider
- Thread-safe implementation
- Proper error handling and logging
- Support for configurable timeouts
- Graceful handling of transient failures
- Clean resource disposal
- XML documentation for public APIs
- Add XML comments for all public APIs
- Include examples in documentation where helpful
- Document exceptions that can be thrown
- Explain complex logic with inline comments
- Update the main README.md if adding features
- Update provider-specific documentation
- Add code examples for new functionality
- GitHub Discussions: For questions and general discussion
- GitHub Issues: For bug reports and feature requests
- Email: stein.gran@protonmail.com
Contributors will be recognized in:
- The project's README
- Release notes
- GitHub's contributor graph
Thank you for contributing to MultiLock! 🎉