risingwavelabs/risingwave · critical

not yet implemented: is_dirty

Error message

not yet implemented: is_dirty

What it means

`JoinSide::is_dirty` in the hash join executor is a deliberate `unimplemented!()` stub, guarded by a comment warning not to call it until implemented. Panics with "not yet implemented: is_dirty" whenever invoked. Currently only reachable from `clear_cache`, which is itself `#[expect(dead_code)]`.

Solutions

  1. Do not call `is_dirty` until it is implemented (see the WARNING comment above it).
  2. Implement `is_dirty` for `JoinSide` (e.g. return whether the degree table/cache has entries) before using it.
  3. If you need cache clearing, implement a check that does not rely on `is_dirty`, or remove the `#[expect(dead_code)]` only after both methods work.

Example fix

// before
fn is_dirty(&self) -> bool {
    unimplemented!()
}
// after
fn is_dirty(&self) -> bool {
    !self.degree_table.is_empty()
}
Defensive patterns

Strategy: try-catch

Try / catch

// The method always panics; only safe pattern is to avoid calling it.
// Until implemented, gate call sites:
// debug_assert!(false, "do not call JoinSide::is_dirty until implemented");
// or feature-gate the caller entirely.

Prevention

When it happens

Trigger: Any code path calling `JoinSide::is_dirty` on a hash-join side — today only the (dead) `clear_cache` method at src/stream/src/executor/hash_join.rs:137; enabling that code path or adding new cache-eviction logic that checks dirtiness.

Common situations: Contributors wiring up join cache clearing or memory management who un-comment/call the guarded method; new tooling or metrics code probing join cache state.

Related errors


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

Appendix: source

Thrown at src/stream/src/executor/hash_join.rs:137

    _marker: std::marker::PhantomData<E>,
}

impl<K: HashKey, S: StateStore, E: JoinEncoding> std::fmt::Debug for JoinSide<K, S, E> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("JoinSide")
            .field("join_key_indices", &self.join_key_indices)
            .field("col_types", &self.all_data_types)
            .field("start_pos", &self.start_pos)
            .field("i2o_mapping", &self.i2o_mapping)
            .field("need_degree_table", &self.need_degree_table)
            .finish()
    }
}

impl<K: HashKey, S: StateStore, E: JoinEncoding> JoinSide<K, S, E> {
    // WARNING: Please do not call this until we implement it.
    fn is_dirty(&self) -> bool {
        unimplemented!()
    }

    #[expect(dead_code)]
    fn clear_cache(&mut self) {
        assert!(
            !self.is_dirty(),
            "cannot clear cache while states of hash join are dirty"
        );

        // TODO: not working with rearranged chain
        // self.ht.clear();
    }

    pub async fn init(&mut self, epoch: EpochPair) -> StreamExecutorResult<()> {
        self.ht.init(epoch).await
    }
}

View on GitHub (pinned to 6469eb736d)