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 __init__(self, basedir, maxlen, *, ancestor=None, logger=None):
      assert maxlen is None or maxlen > 0
      self.basedir = basedir
      self._setup_fs(None)
      self._setup_datadir()
      self.meta = None
      self.buffer = RandomAccessQueue(maxlen=maxlen)
      self.logger = logger
      self.ancestor_meta = None
      if ancestor is not None:
          # Load ancestor as preloaded data
          meta = self._load_ancestor(ancestor, maxlen)
          self.ancestor_meta = meta
  
      # Load or create meta file and share the meta object
      self.meta_file = PersistentRandomAccessQueue._meta_file_name(
          self.basedir
      )
      self._load_meta(ancestor, maxlen)
  
      if self.fs.exists(self.datadir):
          reader = _ChunkReader(self.datadir, self.fs)
          self.gen = reader.read_chunks(maxlen, self.buffer)
  
      else:
          self.gen = 0
          self.fs.makedirs(self.datadir, exist_ok=True)
  
      self.tail = _ChunkWriter(
          self.datadir,
          self.gen,
          self.chunk_size,
          self.fs,
          do_pickle=True,
      )  # Last chunk to be appended
      self.gen += 1
  
      if self.logger:
          self.logger.info(
              "Initial buffer size=%d, next gen=%d",
              len(self.buffer),
              self.gen,
          )
  
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 1 to create a new PersistentRandomAccessQueue object from inherited base classes: object with example: obj = PersistentRandomAccessQueue(basedir, maxlen)