class_docstr: Interface for a storage backend that stores the state of nodes in the cluster.
  
  The storage is thread-safe.
  
  The storage is versioned, which means that each successful stage change to the
  storage will bump the version number. The version number can be used to
  implement optimistic concurrency control.
  
  Each entry in the storage table is also versioned. The version number of an entry
  is the last version number when the entry is updated.
class_docstr_tok: ["Interface","for","a","storage","backend","that","stores","the","state","of","nodes","in","the","cluster",".","The","storage","is","thread-safe",".","The","storage","is","versioned",",","which","means","that","each","successful","stage","change","to","the","storage","will","bump","the","version","number",".","The","version","number","can","be","used","to","implement","optimistic","concurrency","control",".","Each","entry","in","the","storage","table","is","also","versioned",".","The","version","number","of","an","entry","is","the","last","version","number","when","the","entry","is","updated","."]
docstr: Batch update the storage table. This method is atomic.
  
  Args:
      table: The name of the table.
      mutation: A dictionary of key-value pairs to be updated.
      deletion: A list of keys to be deleted.
      expected_storage_version: The expected storage version. The
          update will fail if the version does not match the
          current storage version.
  
  Returns:
      StoreStatus: A tuple of (success, version). If the update is
          successful, returns (True, new_version).
          Otherwise, returns (False, current_version).
docstr_tok: ["Batch","update","the","storage","table",".","This","method","is","atomic",".","Args",":","table",":","The","name","of","the","table",".","mutation",":","A","dictionary","of","key-value","pairs","to","be","updated",".","deletion",":","A","list","of","keys","to","be","deleted",".","expected_storage_version",":","The","expected","storage","version",".","The","update","will","fail","if","the","version","does","not","match","the","current","storage","version",".","Returns",":","StoreStatus",":","A","tuple","of","(","success",",","version",")",".","If","the","update","is","successful",",","returns","(","True",",","new_version",")",".","Otherwise",",","returns","(","False",",","current_version",")","."]
code: def batch_update(
      self,
      table: str,
      mutation: Optional[Dict[str, str]] = None,
      deletion: Optional[List[str]] = None,
      expected_storage_version: Optional[int] = None,
  ) -> StoreStatus:
      """Batch update the storage table. This method is atomic.
  
      Args:
          table: The name of the table.
          mutation: A dictionary of key-value pairs to be updated.
          deletion: A list of keys to be deleted.
          expected_storage_version: The expected storage version. The
              update will fail if the version does not match the
              current storage version.
  
      Returns:
          StoreStatus: A tuple of (success, version). If the update is
              successful, returns (True, new_version).
              Otherwise, returns (False, current_version).
      """
      raise NotImplementedError("batch_update() has to be implemented")
  
file_path: ray/python/ray/autoscaler/v2/instance_manager/storage.py
imports: import copy
  from abc import ABCMeta, abstractmethod
  from collections import defaultdict, namedtuple
  from threading import Lock
  from typing import Dict, List, Optional, Tuple
label_desc: Use image node_id 1 for calling the Storage obj's underlying member method code with example usage: obj.batch_update(table, mutation, deletion, expected_storage_version) without return types