pub async fn rocksdb_with_memory(
        rocksdb: RocksdbStorage,
        batch_diffs: &BatchDiffs,
        l1_batch_number: L1BatchNumber,
    ) -> anyhow::Result<Self> {
        let rocksdb_l1_batch_number = rocksdb.next_l1_batch_number().await;

        match (l1_batch_number + 1).cmp(&rocksdb_l1_batch_number) {
            Ordering::Equal => {
                tracing::debug!(%rocksdb_l1_batch_number, "Using RocksDB-based storage");
                Ok(rocksdb.into())
            }
            Ordering::Greater => {
                let effective_storage_l1_batch_number = l1_batch_number + 1;
                tracing::debug!(
                    %rocksdb_l1_batch_number, %effective_storage_l1_batch_number,
                    "Using RocksDBWithMemory-based storage",
                );

                let storage = RocksdbWithMemory {
                    rocksdb,
                    batch_diffs: batch_diffs.range(rocksdb_l1_batch_number..=l1_batch_number),
                };

                Ok(storage.into())
            }
            Ordering::Less => {
                anyhow::bail!(
                    "RocksDB's L1 batch is #{}, cannot make storage for #{l1_batch_number}",
                    rocksdb_l1_batch_number - 1
                );
            }
        }
    }
Returns a [`ReadStorage`] implementation backed by [`RocksDBWithMemory`].
RocksDB isn't caught up, instead batch diffs are used to fill the gap.
Note, as long as `RocksDBWithMemory` objects are held only for `l1_batch_number >= X`,
then it's ok if RocksDB is caught up in parallel for L1 batch number up to X
because affected storage diff will be read from memory anyway.

# Errors

Propagates RocksDB errors.
core/lib/state/src/storage_factory/mod.rs