risingwavelabs/risingwave · error

bad result from mysql

Error message

bad result from mysql

What it means

Same contract as the sqlite variant: the MySQL-backed election client's try_campaign expects its campaign upsert statement to return exactly one row (the leader row). An empty result is rejected with 'bad result from mysql', meaning the store failed to return the row that should have just been written.

Solutions

  1. Confirm the election table exists in the configured database and is InnoDB with proper primary key.
  2. Check that all meta nodes point to the same MySQL instance/schema (no split-brain across replicas or proxy).
  3. Inspect MySQL error log and meta logs for lock wait/timeout errors around the campaign time.
  4. Retry election; if persistent, verify mysql_async driver version compatibility.
Defensive patterns

Strategy: retry

Try / catch

match client.try_campaign(service, lock_id).await {
    Err(e) if e.to_string().contains("bad result from mysql") => {
        // check connectivity to the same primary instance, then retry
        tokio::time::sleep(Duration::from_secs(1)).await;
    }
    other => other?,
}

Prevention

When it happens

Trigger: try_campaign on MySqlElectionClient when the campaign query returns zero rows despite executing successfully.

Common situations: MySQL server misconfiguration or replication lag wiping the read-back row; using a non-InnoDB table or unusual isolation level for the election table; connection pool pointing at a different database node between write and read.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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

Appendix: source

Thrown at src/meta/src/rpc/election/sql.rs:366

            .await?;

        let query_result = self
            .conn
            .query_one(Statement::from_sql_and_values(
                DatabaseBackend::MySql,
                format!(
                    r#"SELECT service, id, last_heartbeat FROM {table} WHERE service = ?;"#,
                    table = Self::election_table_name(),
                ),
                vec![Value::from(service_name)],
            ))
            .await?;

        let row = query_result
            .map(|query_result| ElectionRow::from_query_result(&query_result, ""))
            .transpose()?;

        let row = row.ok_or_else(|| anyhow!("bad result from mysql"))?;

        Ok(row)
    }

    async fn leader(&self, service_name: &str) -> MetaResult<Option<ElectionRow>> {
        let query_result = self
            .conn
            .query_one(Statement::from_sql_and_values(
                DatabaseBackend::MySql,
                format!(
                    r#"SELECT service, id, last_heartbeat FROM {table} WHERE service = ?;"#,
                    table = Self::election_table_name()
                ),
                vec![Value::from(service_name)],
            ))
            .await?;

        let row = query_result

View on GitHub (pinned to 6469eb736d)