risingwavelabs/risingwave · error

HyperLogLog: Deletion in append-only bucket

Error message

HyperLogLog: Deletion in append-only bucket

What it means

Append-only HyperLogLog buckets only keep a monotonic maximum rank per bucket, which makes retraction (deleting a previously aggregated row) impossible — the correct maximum cannot be recovered after removing a contribution. Any `update` with `retract = true` is rejected.

Source

Thrown at src/expr/impl/src/aggregate/approx_count_distinct/append_only.rs:30

// See the License for the specific language governing permissions and
// limitations under the License.

use risingwave_common::bail;
use risingwave_common_estimate_size::EstimateSize;
use risingwave_expr::Result;

use super::Bucket;

#[derive(Clone, Copy, Default, Debug, EstimateSize, PartialEq, Eq)]
pub struct AppendOnlyBucket(pub u8);

impl Bucket for AppendOnlyBucket {
    fn update(&mut self, index: u8, retract: bool) -> Result<()> {
        if index > 64 || index == 0 {
            bail!("HyperLogLog: Invalid bucket index");
        }
        if retract {
            bail!("HyperLogLog: Deletion in append-only bucket");
        }
        if index > self.0 {
            self.0 = index;
        }
        Ok(())
    }

    fn max(&self) -> u8 {
        self.0
    }
}

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Use the updatable HLL implementation (`UpdatableBucket`/sparse+dense counts) when the input can retract
  2. Configure the source as append-only, or ensure the optimizer proves the stream is append-only before choosing the append-only aggregate
  3. If retracts are spurious, filter them before the aggregate

Example fix

// before: append-only HLL on upsert stream
// agg: approx_count_distinct (append_only)
// after: select updatable variant
// agg: approx_count_distinct (updatable) // supports retract
Defensive patterns

Strategy: fallback

Validate before calling

fn stream_supports_retract(upsert_input: bool) -> Result<()> {
    if upsert_input { bail!("need updatable HLL for retractable input") }
    Ok(())
}

Try / catch

match agg.update(value, retract) {
    Err(e) if e.to_string().contains("Deletion in append-only") => switch_to_updatable_aggregator(),
    other => other,
}

Prevention

When it happens

Trigger: Calling `AppendOnlyBucket::update(index, true)`, i.e. feeding a delete/retract record into an aggregate state built with the append-only HLL implementation.

Common situations: Running aggregations over streams/sources that emit DELETE or UPDATE-with-delete records (e.g. upsert source, backfill retraction) while the plan selected the append-only HLL variant.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/184248b4ee3f1921. Report an issue: GitHub.