diff --git a/linera-storage-service/src/common.rs b/linera-storage-service/src/common.rs index 2dcd874dd344..9db5b6fe1932 100644 --- a/linera-storage-service/src/common.rs +++ b/linera-storage-service/src/common.rs @@ -74,6 +74,15 @@ impl From for StorageServiceStoreError { impl KeyValueStoreError for StorageServiceStoreError { const BACKEND: &'static str = "service"; + + fn must_reload_view(&self) -> bool { + // A `write_batch` is sent as one or more gRPC calls. If the round-trip fails + // (a gRPC status such as `DEADLINE_EXCEEDED`/`UNAVAILABLE`, or a transport-level + // error) the server may or may not have applied the batch, so the in-memory view + // must be reloaded from storage. These variants can also surface on read RPCs, + // where a reload is unnecessary but harmless; we err on the side of reloading. + matches!(self, Self::GrpcError(_) | Self::TransportError(_)) + } } /// Returns the storage service endpoint used for testing, read from the environment. diff --git a/linera-views/src/backends/rocks_db.rs b/linera-views/src/backends/rocks_db.rs index 1d64f9896f82..e3c1565e4d24 100644 --- a/linera-views/src/backends/rocks_db.rs +++ b/linera-views/src/backends/rocks_db.rs @@ -282,7 +282,9 @@ impl RocksDbStoreExecutor { full_key[0] = STORED_ROOT_KEYS_PREFIX; inner_batch.put(&full_key, vec![]); } - self.db.write(inner_batch)?; + self.db + .write(inner_batch) + .map_err(RocksDbStoreInternalError::WriteBatchError)?; Ok(()) } } @@ -711,6 +713,12 @@ pub enum RocksDbStoreInternalError { #[error("RocksDB error: {0}")] RocksDb(#[from] rocksdb::Error), + /// RocksDB error while writing a batch. Unlike [`RocksDbStoreInternalError::RocksDb`] + /// (which also covers read failures), this error means a batch write may or may not + /// have been applied, so the in-memory view must be reloaded from storage. + #[error("RocksDB write-batch error: {0}")] + WriteBatchError(rocksdb::Error), + /// The database contains a file which is not a directory #[error("Namespaces should be directories")] NonDirectoryNamespace, @@ -777,6 +785,10 @@ impl Eq for PathWithGuard {} impl KeyValueStoreError for RocksDbStoreInternalError { const BACKEND: &'static str = "rocks_db"; + + fn must_reload_view(&self) -> bool { + matches!(self, Self::WriteBatchError(_)) + } } /// The composed error type for the `RocksDbStore`