class_docstr: Persistent data structure for replay buffer
  
  Features:
  - Perfectly compatible with collections.RandomAccessQueue
  - Persist replay buffer data on storage to survive restart
    - [todo] remove popleft'd data from disk
  - Reuse replay buffer data to another training session
    - Track back the replay buffer lineage
  Non-it-is-for:
  - Extend replay buffer by spilling to the disk
  
  TODOs
  - Optimize writes; buffered writes with threads or something
  
  Arguments:
      basedir (str): Path to the directory where replay buffer data is stored.
      maxlen (int): Max length of queue. Appended data beyond
          this limit is only removed from memory, not from storage.
      ancestor (str): Path to pre-generated replay buffer.
      logger: logger
class_docstr_tok: ["Persistent","data","structure","for","replay","buffer","Features",":","-","Perfectly","compatible","with","collections.RandomAccessQueue","-","Persist","replay","buffer","data","on","storage","to","survive","restart","-","[","todo","]","remove","popleft","'d","data","from","disk","-","Reuse","replay","buffer","data","to","another","training","session","-","Track","back","the","replay","buffer","lineage","Non-it-is-for",":","-","Extend","replay","buffer","by","spilling","to","the","disk","TODOs","-","Optimize","writes",";","buffered","writes","with","threads","or","something","Arguments",":","basedir","(","str",")",":","Path","to","the","directory","where","replay","buffer","data","is","stored",".","maxlen","(","int",")",":","Max","length","of","queue",".","Appended","data","beyond","this","limit","is","only","removed","from","memory",",","not","from","storage",".","ancestor","(","str",")",":","Path","to","pre-generated","replay","buffer",".","logger",":","logger"]
code: def _load_meta(self, ancestor, maxlen):
      # This must be checked by single process to avoid race
      # condition where one creates and the other may detect it
      # as exisiting... process differently OTL
      if self.fs.exists(self.meta_file):
          # Load existing meta
          with self.fs.open(self.meta_file, "rb") as fp:
              self.meta = pickle.load(fp)
  
          # TODO: update chunksize and other properties
          assert isinstance(self.meta, dict)
  
          # MPI world size must be the same when it's restart
          assert (
              self.meta["comm_size"] == self.comm_size
          ), "Reloading same basedir requires same comm.size"
  
      else:
          # Create meta from scratch
          # Timestamp from pfrl.experiments.prepare_output_dir
  
          ts = datetime.strftime(datetime.today(), "%Y%m%dT%H%M%S.%f")
          self.meta = dict(
              basedir=self.basedir,
              maxlen=maxlen,
              comm_size=self.comm_size,
              ancestor=ancestor,
              timestamp=ts,
              chunksize=self.chunk_size,
              trim=False,  # `trim` is reserved for future extension.
          )
  
          # Note: If HDFS access fails at first open, make sure
          # no ``cv2`` import fail happening - failing
          # ``opencv-python`` due to lacking ``libSM.so`` may
          # break whole dynamic library loader and thus breaks
          # other dynamic library loading (e.g. libhdfs.so)
          # which may happen here. Solution for this is to let
          # the import success, e.g. installing the lacking
          # library correctly.
          self.fs.makedirs(self.basedir, exist_ok=True)
          with self.fs.open(self.meta_file, "wb") as fp:
              pickle.dump(self.meta, fp)
  
imports: import binascii
  import collections
  import os
  import pickle
  from datetime import datetime
  from struct import calcsize, pack, unpack
  from pfrl.collections.random_access_queue import RandomAccessQueue
label_desc: Use image node_id 2 for calling the PersistentRandomAccessQueue obj's underlying member method code with example usage: obj._load_meta(ancestor, maxlen) without return types