Code Quiz

Deletable Bloom Filter Gone Wrong

A Bloom filter chosen for a cache-invalidation feature adds a remove() method that silently corrupts membership queries.

Codejavascript
// Requirement: track 'seen' URLs, but ALSO support un-seeing (removing) URLs.
// Team picked a standard Bloom filter and bolted on remove().
class BloomFilter {
  constructor(size, k) {
    this.size = size;
    this.k = k;
    this.bits = new Uint8Array(size);
  }

  _hashes(str) {
    const out = [];
    let h1 = 2166136261;
    let h2 = 5381;
    for (const ch of str) {
      h1 = (h1 ^ ch.charCodeAt(0)) * 16777619 >>> 0;
      h2 = ((h2 << 5) + h2 + ch.charCodeAt(0)) >>> 0;
    }
    for (let i = 0; i < this.k; i++) {
      out.push((h1 + i * h2) % this.size);
    }
    return out;
  }

  add(str) {
    for (const idx of this._hashes(str)) this.bits[idx] = 1;
  }

  remove(str) {
    for (const idx of this._hashes(str)) this.bits[idx] = 0;
  }

  has(str) {
    return this._hashes(str).every(idx => this.bits[idx] === 1);
  }
}

Given the requirement to support removal, what is the bug in this implementation?