BloomFilter.NetCore

repository·main·Indexed 42 days ago

https://github.com/vla/bloomfilter.netcore

A high-performance .NET library for Bloom filters providing probabilistic membership testing with O(1) time complexity. It supports in-memory implementations using BitArray and distributed implementations via Redis backends including StackExchange.Redis, CSRedisCore, FreeRedis, and EasyCaching. Features include a fluent API for configuration (v3.0+), ASP.NET Core dependency injection integration, and support for over 20 hash algorithms such as XXHash3 and Murmur3.

Tokens
15.5K
Snippets
42
Records
51
Agent score
3%

What's inside BloomFilter.NetCore

  1. Overview of BloomFilter.NetCore

    main

    BloomFilter.NetCore is a high-performance .NET library for Bloom filters—probabilistic data structures used to test element membership with minimal memory footprint. It provides $O(1)$ time complexity for both add and query operations.

    Key characteristics:

    • Space Efficient: Uses significantly less memory than a HashSet.
    • Probabilistic: May return false positives (claiming an element is present when it is not) but never false negatives.
    • Two Implementation Types:
      1. In-Memory (FilterMemory): Uses BitArray for single-process scenarios.
      2. Distributed (FilterRedis series): Uses Redis backends to share state across multiple application instances.

    Common use cases include cache penetration protection, deduplication (URLs, IDs), recommendation systems, and web crawlers.

  2. Choose between In-Memory and Redis modes

    main

    Select the backend mode based on your application architecture:

    ScenarioRecommended ModeReason
    Single-instance applicationIn-MemoryHighest performance, no network overhead
    Multi-instance applicationRedisShared state, distributed support
    Persistence requiredRedisRedis provides persistence
    Temporary deduplicationIn-MemorySimple and fast
    Cross-service sharingRedisMulti-language access support
  3. Choose a hash algorithm for BloomFilter.NetCore

    main

    BloomFilter.NetCore supports over 20 hash algorithms. Your choice should be based on the trade-off between performance (speed) and accuracy (false positive rate).

    Algorithm Categories

    • XXHash Family (XXHash3, XXHash64, etc.): Fastest and excellent quality. Recommended for high-performance needs.
    • Murmur Family (Murmur3, etc.): Recommended for production. Good balance of performance and quality.
    • Checksum (CRC32, CRC64, Adler32): Balanced performance and quality for general scenarios.
    • Cryptographic (SHA1, SHA256, etc.): Highest quality but slowest. Use only when extreme security is required.
    • RNG-based: High quality but slower.
    • LCG-based: Extremely fast but lower quality; use only if you can tolerate high false positive rates.
    // Recommended: Default Murmur3 for production
    var bf1 = FilterBuilder.Build(10_000_000, 0.01, HashMethod.Murmur3);
    
    // High Performance: Use XXHash3
    var bf2 = FilterBuilder.Build(10_000_000, 0.01, HashMethod.XXHash3);
    
    // High Precision: Use SHA256 with a lower error rate
    var bf3 = FilterBuilder.Build(10_000_000, 0.0001, HashMethod.SHA256);
    
    // Distributed: Use XXHash64 for Redis
    var bf4 = FilterRedisBuilder.Build("localhost:6379", "MyFilter", 10_000_000, 0.01, HashMethod.XXHash64);
  4. Common BloomFilter Use Cases

    main

    1. Prevent Cache Penetration

    Use a BloomFilter as the first line of defense. If Contains(id) returns false, the item definitely does not exist, so you can return null immediately without querying the database or cache.

    2. URL Deduplication (Web Crawlers)

    Maintain a BloomFilter of visited URLs. Before crawling a new URL, check Contains(url). If it returns true, skip it; otherwise, add it to the filter and proceed.

    3. Distributed Deduplication

    In multi-instance microservices, use a Redis-backed BloomFilter. All instances check the same shared filter (e.g., ContainsAsync(messageId)) to ensure a specific message or event is only processed once across the entire cluster.

  5. Calculate Bloom filter memory usage

    main

    Memory usage is determined by the capacity (m) in bits. The formula for memory in bytes is:

    Memory (bytes) = m / 8

    You can inspect the Capacity property on a Filter instance to calculate usage.

    // 10 million elements, 1% false positive rate
    var bf = FilterBuilder.Build(10_000_000, 0.01);
    var filter = (Filter)bf;
    
    // Calculate memory usage
    long bits = filter.Capacity;
    long bytes = bits / 8;
    double mb = bytes / (1024.0 * 1024.0);
    
    Console.WriteLine($"Bit array size: {bits:N0} bits");
    Console.WriteLine($"Memory usage: {bytes:N0} bytes ({mb:F2} MB)");
    // Output: approximately 11.4 MB
  6. Thread safety and concurrency

    main

    BloomFilter.NetCore is thread-safe. You can safely perform Add and Contains operations across multiple threads concurrently.

    var bf = FilterBuilder.Build(10_000_000, 0.01);
    
    // Concurrent additions
    Parallel.For(0, 1000, i =>
    {
        bf.Add($"item:{i}");
    });
    
    // Concurrent checks
    Parallel.For(0, 1000, i =>
    {
        var exists = bf.Contains($"item:{i}");
    });
  7. Note on element deletion

    main

    Standard Bloom Filters do not support deleting elements. Deleting an element might inadvertently affect the detection of other elements because multiple elements can map to the same bits.

    If your use case requires deletion, consider using a Counting Bloom Filter or a Cuckoo Filter.

  8. How BloomFilter.NetCore works

    main

    BloomFilter.NetCore is a high-performance .NET library providing Bloom Filters for both single-process (in-memory) and distributed (Redis) environments.

    Core Abstractions

    • IBloomFilter: The primary interface for all operations.
    • Filter (Base Class): Provides mathematical logic for calculating optimal parameters (m, k, n, p) and implements core logic.
    • FilterMemory: An in-memory implementation using BitArray. It handles large capacities (over 2GB) by automatically splitting data into multiple BitArray buckets.
    • Redis Implementations: Distributed implementations (FilterRedis, FilterCSRedis, FilterFreeRedis, FilterEasyCachingRedis) that use Redis SETBIT/GETBIT commands, allowing multiple application instances to share the same filter state.

    Key Features

    • Mathematical Optimization: Automatically calculates the best bit array size (m) and hash count (k) based on your expected element count and desired error rate.
    • Concurrency: Uses AsyncLock (based on SemaphoreSlim) to ensure thread safety for in-memory operations and supports distributed concurrency via Redis.
    • Performance: Utilizes modern .NET features like Span<T>, ReadOnlyMemory<T>, and unsafe code blocks for high-speed, zero-copy operations.
  9. Implementation Patterns: Cache Penetration and Deduplication

    main

    Bloom filters are effective for several real-world scenarios:

    Cache Penetration Protection

    Use a Bloom filter as a first layer before querying a cache or database. If Contains(id) is false, the item definitely does not exist, allowing you to return early and protect downstream resources.

    URL Deduplication

    In web crawlers, use a Bloom filter to track visitedUrls. Before processing a URL, check Contains(url). If true, skip it; if false, add it and proceed.

    Distributed Deduplication

    When running multiple service instances, use a Redis-backed Bloom filter. This allows all instances to share a global state (e.g., GlobalDeduplication) to ensure a message or task is only processed once across the entire cluster.

  10. Thread-safety and concurrent access

    main

    BloomFilter.NetCore is thread-safe. You can perform Add and Contains operations concurrently from multiple threads (e.g., using Parallel.For) without external synchronization.

    // Multi-threaded concurrent access
    var bf = FilterBuilder.Build(10_000_000, 0.01);
    
    Parallel.For(0, 1000, i =>
    {
        bf.Add($"item:{i}"); // Thread-safe
    });
    
    Parallel.For(0, 1000, i =>
    {
        var exists = bf.Contains($"item:{i}"); // Thread-safe
    });
  11. Choose between Memory mode and Redis mode

    main

    Select the backend based on your application architecture:

    ScenarioRecommended ModeReason
    Single-instance applicationMemory modeHighest performance, no network overhead
    Multi-instance applicationRedis modeShared state, supports distributed environments
    Requires persistenceRedis modeRedis provides persistence
    Temporary deduplicationMemory modeSimple and fast
    Cross-service sharingRedis modeSupports multi-language access
  12. Choose a Hash Algorithm

    main

    BloomFilter.NetCore supports over 20 hash algorithms. Your choice should balance performance and accuracy based on your specific use case.

    Algorithm Categories

    • XXHash Series (Recommended for Performance): XXHash3, XXHash128, XXHash64, XXHash32. These are the fastest with excellent quality. Use XXHash3 for extreme performance needs.
    • Murmur Series (Recommended for Production): Murmur3, Murmur128BitsX64, Murmur128BitsX86. Provides a great balance of performance and quality.
    • Checksums: CRC64, CRC32, Adler32. Balanced performance and quality for general scenarios.
    • Cryptographic Hashes (Highest Quality, Slowest): SHA256, SHA512, SHA384, SHA1. Use these only when extremely high security/quality is required.
    • LCG/RNG Classes: LCGWithFNV1, RNGWithFNV1, etc. Very fast but lower quality; use only if performance is critical and high false positive rates are tolerable.
    // Recommended: Murmur3 for production (balance of performance and quality)
    var bf1 = FilterBuilder.Build(10_000_000, 0.01, HashMethod.Murmur3);
    
    // High Performance: XXHash3
    var bf2 = FilterBuilder.Build(10_000_000, 0.01, HashMethod.XXHash3);
    
    // High Precision: SHA256 + lower error rate
    var bf3 = FilterBuilder.Build(10_000_000, 0.0001, HashMethod.SHA256);
    
    // Distributed (Redis): XXHash64 is recommended for speed and cross-language support
    var bf4 = FilterRedisBuilder.Build(
        "localhost:6379",
        "MyFilter",
        10_000_000,
        0.01,
        HashMethod.XXHash64
    );