1. 24 Sep, 2015 1 commit
  2. 11 Sep, 2015 1 commit
  3. 02 Sep, 2015 2 commits
  4. 18 Aug, 2015 2 commits
    • Kirill Smelkov's avatar
      bigfile/zodb: ZBlk._p_invalidate() can be called more than once, in particular in case of conflicts · 800c14a9
      Kirill Smelkov authored
      When there is a conflict (on any object, but on ZBlk in particular) ZODB
      machinery calls its ._p_invalidate() twice:
      
        File ".../wendelin.core/bigfile/tests/test_filezodb.py", line 661, in test_bigfile_filezodb_vs_conflicts
          tm2.commit()    # this should raise ConflictError and stay at 11 state
        File ".../transaction/_manager.py", line 111, in commit
          return self.get().commit()
        File ".../transaction/_transaction.py", line 271, in commit
          self._commitResources()
        File ".../transaction/_transaction.py", line 414, in _commitResources
          self._cleanup(L)
        File ".../transaction/_transaction.py", line 426, in _cleanup
          rm.abort(self)
        File ".../ZODB/Connection.py", line 436, in abort
          self._abort()
        File ".../ZODB/Connection.py", line 479, in _abort
          self._cache.invalidate(oid)
        File ".../wendelin.core/bigfile/file_zodb.py", line 148, in _p_invalidate
          traceback.print_stack()
      
      and
      
        File ".../wendelin.core/bigfile/tests/test_filezodb.py", line 661, in test_bigfile_filezodb_vs_conflicts
          tm2.commit()    # this should raise ConflictError and stay at 11 state
        File ".../transaction/_manager.py", line 111, in commit
          return self.get().commit()
        File ".../transaction/_transaction.py", line 271, in commit
          self._commitResources()
        File ".../transaction/_transaction.py", line 416, in _commitResources
          self._synchronizers.map(lambda s: s.afterCompletion(self))
        File ".../transaction/weakset.py", line 59, in map
          f(elt)
        File ".../transaction/_transaction.py", line 416, in <lambda>
          self._synchronizers.map(lambda s: s.afterCompletion(self))
        File ".../ZODB/Connection.py", line 831, in _storage_sync
          self._flush_invalidations()
        File ".../ZODB/Connection.py", line 539, in _flush_invalidations
          self._cache.invalidate(invalidated)
        File ".../wendelin.core/bigfile/file_zodb.py", line 148, in _p_invalidate
          traceback.print_stack()
      
      i.e. first invalidation is done by commit cleanup:
      
          https://github.com/zopefoundation/transaction/blob/1.4.4/transaction/_transaction.py#L414
          https://github.com/zopefoundation/ZODB/blob/3.10/src/ZODB/Connection.py#L479
      
      and then Connection.afterCompletion() flushes invalidation again:
      
          https://github.com/zopefoundation/transaction/blob/1.4.4/transaction/_transaction.py#L416
          https://github.com/zopefoundation/ZODB/blob/3.10/src/ZODB/Connection.py#L833
          https://github.com/zopefoundation/ZODB/blob/3.10/src/ZODB/Connection.py#L539
      
      If there was no conflict - there will be no ConflictError raised and
      thus no Transaction._cleanup() done in its ._commitResources() ->
      invalidation called only once. But with ConflictError - it is twice.
      
      Adjust ZBlk._p_invalidate() not to delve into real invalidation more
      than once - else we will fail, as ZBlk._v_zfile becomes unbound after
      invalidation done the first time.
      800c14a9
    • Kirill Smelkov's avatar
      bigfile/zodb: Note that even LivePersistent goes to GHOST state on invalidation · 48b2bb74
      Kirill Smelkov authored
      LivePersistent can go to ghost state, because invalidation cannot be
      ignored, i.e. they indicate the object has been changed externally.
      
      This does not break our logic for ZBigFile and ZBigArray as
      invalidations can happen only at transaction boundary, so during the
      course of transaction those classes are guaranteed to stay uptodate and
      thus not loose ._v_file and ._v_fileh (which is the reason they inherit
      from LivePersistent).
      
      it is ok to loose ._v_file and ._v_fileh at transaction boundary and
      become ghost - those objects will be recreated upon going back uptodate
      and will stay alive again during the whole transaction window.
      
      We care only not to loose e.g. ._v_fileh inside transaction, because
      loosing that data manager and thus data it manages inside transaction
      can break synchronization logic and forget changed-through-mmap data.
      48b2bb74
  5. 17 Aug, 2015 5 commits
    • Kirill Smelkov's avatar
      bigfile/zodb: Fix typos · 26d5b35e
      Kirill Smelkov authored
      26d5b35e
    • Kirill Smelkov's avatar
      bigfile/zodb: Do not hold reference to ZBigFileH indefinitely in Connection.onOpenCallback · 059c71e1
      Kirill Smelkov authored
      If we do - ZBigFileH objects just don't get garbage collected, and
      sooner or later this way it leaks enough filedescriptors so that main
      zope loop breaks:
      
          Traceback (most recent call last):
            File ".../bin/runzope", line 194, in <module>
              sys.exit(Zope2.Startup.run.run())
            File ".../eggs/Zope2-2.13.22-py2.7.egg/Zope2/Startup/run.py", line 26, in run
              starter.run()
            File ".../eggs/Zope2-2.13.22-py2.7.egg/Zope2/Startup/__init__.py", line 105, in run
              Lifetime.loop()
            File ".../eggs/Zope2-2.13.22-py2.7.egg/Lifetime/__init__.py", line 43, in loop
              lifetime_loop()
            File ".../eggs/Zope2-2.13.22-py2.7.egg/Lifetime/__init__.py", line 53, in lifetime_loop
              asyncore.poll(timeout, map)
            File ".../parts/python2.7/lib/python2.7/asyncore.py", line 145, in poll
              r, w, e = select.select(r, w, e, timeout)
          ValueError: filedescriptor out of range in select()
      
          $ lsof -p <runzope-pid> |grep ramh | wc -l
          950
      
      So continuing 64d1f40b (bigfile/zodb: Monkey-patch for ZODB.Connection
      to support callback on .open()) let's change the implementation to use
      WeakSet for callbacks list.
      
      Yes, because weakref to bound methods release immediately, we give up
      flexibility to subscribe to arbitrary callbacks. If it become an issue,
      something like WeakMethod from py3 or recipes from the net how to do it
      are there.
      059c71e1
    • Kirill Smelkov's avatar
      Drop support for ZODB < 3.10 · 105ab1c7
      Kirill Smelkov authored
      ZODB 3.10.4 was released almost 4 years ago, and contains significant
      change how ghost objects coming from DB are initially setup.
      105ab1c7
    • Kirill Smelkov's avatar
      bigfile: ZODB -> BigFileH invalidate propagation · 92bfd03e
      Kirill Smelkov authored
      Continuing theme from the previous patch, here is propagation of
      invalidation messages from ZODB to BigFileH memory.
      
      The use-case here is that e.g. one fileh mapping was created in one
      connection, another in another, and after doing changes in second
      connection and committing there, the first fileh has to invalidate
      appropriate already-loaded pages, so its next transaction won't work
      with stale data.
      
      To do it, we hook into ZBlk._p_invalidate() and propagate the
      invalidation message to ZBigFile which then notifies all
      opened-through-it ZBigFileH to invalidate a page.
      
      ZBlk -> ZBigFile lookup is done without storing backpointer in ZODB -
      instead, every time ZBigFile touches ZBlk object (and thus potentially
      does GHOST -> Live transition to it), we (re-)bind it back to ZBigFile.
      Since ZBigFile is the only class that works with ZBlk objects it is safe
      to do so.
      
      For ZBigFile to notify "all-opened-through-it" ZBigFileH, a weakset is
      introduced to track them.
      
      Otherwise the real page invalidation work is done by virtmem (see
      previous patch).
      92bfd03e
    • Kirill Smelkov's avatar
      bigfile/virtmem: Client API to invalidate a fileh page · cb779c7b
      Kirill Smelkov authored
      FileH is a handle representing snapshot of a file. If, for a pgoffset,
      fileh already has loaded page, but we know the content of the file has
      changed externally after loading has been done, we need to propagate to
      fileh that such-and-such page should be invalidated (and reloaded on
      next access).
      
      This patch introduces
      
          fileh_invalidate_page(fileh, pgoffset)
      
      to do just that.
      
      In the next patch we'll use this facility to propagate invalidations of
      ZBlk ZODB objects to virtmem subsystem.
      
      NOTE
      
      Since invalidation removes "dirtiness" from a page state, several
      subsequent invalidations can make a fileh completely non-dirty
      (invalidating all dirty page). Previously fileh->dirty was just a one
      bit, so we needed to improve how we track dirtiness.
      
      One way would be to have a dirty list for fileh pages and operate on
      that. This has advantage to even optimize dirty pages processing like
      fileh_dirty_writeout() where we currently scan through all fileh pages
      just to write only PAGE_DIRTY ones.
      
      Another simpler way is to make fileh->dirty a counter and maintain that.
      
      Since we are going to move virtmem subsystem back into the kernel, here,
      a simpler less-intrusive approach is used.
      cb779c7b
  6. 12 Aug, 2015 3 commits
    • Kirill Smelkov's avatar
      bigfile/zodb: ZODB.Connection can migrate between threads on close/open and we have to care · c7c01ce4
      Kirill Smelkov authored
      Intro
      -----
      
      ZODB maintains pool of opened-to-DB connections. For each request Zope
      opens 1 connection and, after request handling is done, returns the
      connection back to ZODB pool (via Connection.close()). The same
      connection will be opened again for handling some future next request at
      some future time. This next open can happen in different-from-first
      request worker thread.
      
      TransactionManager  (as accessed by transaction.{get,commit,abort,...})
      is thread-local, that is e.g. transaction.get() returns different
      transaction for threads T1 and T2.
      
      When _ZBigFileH hooks into txn_manager to get a chance to run its
      .beforeCompletion() when transaction.commit() is run, it hooks into
      _current_ _thread_ transaction manager.
      
      Without unhooking on connection close, and circumstances where
      connection migrates to different thread this can lead to
      dissynchronization between ZBigFileH managing fileh pages and Connection
      with ZODB objects. And even to data corruption, e.g.
      
          T1              T2
      
          open
          zarray[0] = 11
          commit
          close
      
                          open                # opens connection as closed in T1
          open
                          zarray[0] = 21
          commit
                          abort
      
          close           close
      
      Here zarray[0]=21 _will_ be committed by T1 as part of T1 transaction -
      because when T1 does commit .beforeCompletion() for zarray is invoked,
      sees there is dirty data and propagate changes to zodb objects in
      connection for T2, joins connection for T2 into txn for T1, and then txn
      for t1 when doing two-phase-commit stores modified objects to DB ->
      oops.
      
      ----------------------------------------
      
      To prevent such dissynchronization _ZBigFileH needs to be a DataManager
      which works in sync with the connection it was initially created under -
      on connection close, unregister from transaction_manager, and on
      connection open, register to transaction manager in current, possibly
      different, thread context. Then there won't be incorrect
      beforeCompletion() notification and corruption.
      
      This issue, besides possible data corruption, was probably also exposing
      itself via following ways we've seen in production (everywhere
      connection was migrated from T1 to T2):
      
      1. Exception ZODB.POSException.ConnectionStateError:
              ConnectionStateError('Cannot close a connection joined to a transaction',)
              in <bound method Cleanup.__del__ of <App.ZApplication.Cleanup instance at 0x7f10f4bab050>> ignored
      
           T1          T2
      
                       modify zarray
                       commit/abort    # does not join zarray to T2.txn,
                                       # because .beforeCompletion() is
                                       # registered in T1.txn_manager
      
           commit                      # T1 invokes .beforeCompletion()
           ...                         # beforeCompletion() joins ZBigFileH and zarray._p_jar (=T2.conn) to T1.txn
           ...                         # commit is going on in progress
           ...
           ...         close           # T2 thinks request handling is done and
           ...                         # and closes connection. But T2.conn is
           ...                         # still joined to T1.txn
      
      2. Traceback (most recent call last):
           File ".../wendelin/bigfile/file_zodb.py", line 121, in storeblk
             def storeblk(self, blk, buf):   return self.zself.storeblk(blk, buf)
           File ".../wendelin/bigfile/file_zodb.py", line 220, in storeblk
             zblk._v_blkdata = bytes(buf)    # FIXME does memcpy
           File ".../ZODB/Connection.py", line 857, in setstate
             raise ConnectionStateError(msg)
         ZODB.POSException.ConnectionStateError: Shouldn't load state for 0x1f23a5 when the connection is closed
      
         Similar to "1", but close in T2 happens sooner, so that when T1 does
         the commit and tries to store object to database, Connection refuses to
         do the store:
      
           T1          T2
      
                       modify zarray
                       commit/abort
      
           commit
           ...         close
           ...
           ...
           . obj.store()
           ...
           ...
      
      3. Traceback (most recent call last):
           File ".../wendelin/bigfile/file_zodb.py", line 121, in storeblk
             def storeblk(self, blk, buf):   return self.zself.storeblk(blk, buf)
           File ".../wendelin/bigfile/file_zodb.py", line 221, in storeblk
             zblk._p_changed = True          # if zblk was already in DB: _p_state -> CHANGED
           File ".../ZODB/Connection.py", line 979, in register
             self._register(obj)
           File ".../ZODB/Connection.py", line 989, in _register
             self.transaction_manager.get().join(self)
           File ".../transaction/_transaction.py", line 220, in join
             Status.ACTIVE, Status.DOOMED, self.status))
         ValueError: expected txn status 'Active' or 'Doomed', but it's 'Committing'
      
        ( storeblk() does zblk._p_changed -> Connection.register(zblk) ->
          txn.join() but txn is already committing
      
          IOW storeblk() was invoked with txn.state being already 'Committing' )
      
          T1          T2
      
                      modify obj      # this way T2.conn joins T2.txn
                      modify zarray
      
          commit                      # T1 invokes .beforeCompletion()
          ...                         # beforeCompletion() joins only _ZBigFileH to T1.txn
          ...                         # (because T2.conn is already marked as joined)
          ...
          ...         commit/abort    # T2 does commit/abort - this touches only T2.conn, not ZBigFileH
          ...                         # in particular T2.conn is now reset to be not joined
          ...
          . tpc_begin                 # actual active commit phase of T1 was somehow delayed a bit
          . tpc_commit                # when changes from RAM propagate to ZODB objects associated
          .  storeblk                 # connection (= T2.conn !) is notified again,
          .   zblk = ...              # wants to join txn for it thinks its transaction_manager,
                                      # which when called from under T1 returns *T1* transaction manager for
                                      # which T1.txn is already in state='Committing'
      
      4. Empty transaction committed to NEO
      
         ( different from doing just transaction.commit() without changing
           any data - a connection was joined to txn, but set of modified
           object turned out to be empty )
      
         This is probably a race in Connection._register when both T1 and T2
         go to it at the same time:
      
         https://github.com/zopefoundation/ZODB/blob/3.10/src/ZODB/Connection.py#L988
      
         def _register(self, obj=None):
              if self._needs_to_join:
                  self.transaction_manager.get().join(self)
                  self._needs_to_join = False
      
          T1                          T2
      
                                      modify zarray
          commit
          ...
          .beforeCompletion           modify obj
          . if T2.conn.needs_join      if T2.conn.needs_join      # race here
          .   T2.conn.join(T1.txn)       T2.conn.join(T2.txn)     # as a result T2.conn joins both T1.txn and T2.txn
          .
          commit finishes             # T2.conn registered-for-commit object list is now empty
      
                                      commit
                                       tpc_begin
                                        storage.tpc_begin
                                       tpc_commit
                                        # no object stored, because for-commit-list is empty
      
      /cc @jm, @klaus, @Tyagov, @vpelletier
      c7c01ce4
    • Kirill Smelkov's avatar
      bigfile/zodb: Monkey-patch for ZODB.Connection to support callback on .open() · 64d1f40b
      Kirill Smelkov authored
      ZODB.Connection has support for calling callbacks on .close() but not on
      .open() . We'll need to hook into both Connection open/close process in the
      next patch (for _ZBigFileH to stay in sync with Connection state).
      
      NOTE
      
      on-open  callbacks are setup once and fire many times on every open,
      on-close callbacks are setup once and fire only once on next close.
      
      The reason for this is that on-close callbacks are useful for scheduling
      current connection cleanup, after its processing is done. But on-open
      callback is for future connection usage, which is generally not related
      to current connection.
      
      /cc @jm, @vpelletier
      64d1f40b
    • Kirill Smelkov's avatar
  7. 09 Aug, 2015 1 commit
    • Kirill Smelkov's avatar
      bigfile/py: Teach storeblk() how to correctly propagate traceback on error · 6da5172e
      Kirill Smelkov authored
      Previously we were limited to printing traceback starting down from just
      storeblk() via explicit PyErr_PrintEx() - because pybuf was attached to
      memory which could go away right after return from C function - so we
      had to destroy that object for sure, not letting any traceback to hold a
      reference to it.
      
      This turned out to be too limiting and not showing full context where
      errors happen.
      
      So do the following trick: before returning, reattach pybuf to empty
      region at NULL, and this way we don't need to worry about pybuf pointing
      to memory which can go away -> thus instead of printing exception locally
      - just return it the usual way it is done with C api in Python.
      
      NOTE In contrast to PyMemoryViewObject, PyBufferObject definition is not
      public, so to support Python2 - had to copy its definition to PY2 compat
      header.
      
      NOTE2 loadblk() is not touched - the loading is done from sighandler
      context, which simulates as if it work in separate python thread, so it
      is leaved as is for now.
      6da5172e
  8. 06 Aug, 2015 4 commits
    • Kirill Smelkov's avatar
      bigfile/virtmem: Big Virtmem lock · d53271b9
      Kirill Smelkov authored
      At present several threads running can corrupt internal virtmem
      datastructures (e.g. ram->lru_list, fileh->pagemap, etc).
      
      This can happen even if we have zope instances only with 1 worker thread
      - because there are other "system" thread, and python garbage collection
      can trigger at any thread, so if a virtmem object, e.g. VMA or FileH was
      there sitting at GC queue to be collected, their collection, and thus
      e.g. vma_unmap() and fileh_close() will be called from
      different-from-worker thread.
      
      Because of that virtmem just has to be aware of threads not to allow
      internal datastructure corruption.
      
      On the other hand, the idea of introducing userspace virtual memory
      manager turned out to be not so good from performance and complexity
      point of view, and thus the plan is to try to move it back into the
      kernel. This way it does not make sense to do a well-optimised locking
      implementation for userspace version.
      
      So we do just a simple single "protect-all" big lock for virtmem.
      
      Of a particular note is interaction with Python's GIL - any long-lived
      lock has to be taken with GIL released, because else it can deadlock:
      
          t1  t2
      
          G
          V   G
         !G   V
          G
      
      so we introduce helpers to make sure the GIL is not taken, and to retake
      it back if we were holding it initially.
      
      Those helpers (py_gil_ensure_unlocked / py_gil_retake_if_waslocked) are
      symmetrical opposites to what Python provides to make sure the GIL is
      locked (via PyGILState_Ensure / PyGILState_Release).
      
      Otherwise, the patch is more-or-less straightforward application for
      one-big-lock to protect everything idea.
      d53271b9
    • Kirill Smelkov's avatar
      bigfile: Simple test that we can handle GC from-under sighandler · 786d418d
      Kirill Smelkov authored
      And specifically that GC'ed object __del__ calls into virtmem
      (vma_dealloc and fileh_dealloc) again.
      
      NOTE not sure it is a good idea to do GC from under sighandle, but
           currently it happens in practice, because we did not cared to
           protect against it.
      786d418d
    • Kirill Smelkov's avatar
      bigfile/virtmem: When restoring SIGSEGV, don't change procmask for other signals · d7c33cd7
      Kirill Smelkov authored
      We factored out SIGSEGV block/restore from fileh_dirty_writeout() to all
      functions in cb7a7055 (bigfile/virtmem: Block/restore SIGSEGV in
      non-pagefault-handling function). The restoration however just sets
      whole thread sigmask.
      
      It could be possible that between block/restore calls procmask for other
      signals could be changed, and this way - setting procmask directly - we
      will overwrite them.
      
      So be careful, and when restoring SIGSEGV mask, touch mask bit for only
      that signal.
      
      ( we need xsigismember helper to get this done, which is also introduced
        in this patch )
      d7c33cd7
    • Kirill Smelkov's avatar
      bigfile/virtmem: Block/restore SIGSEGV in non-pagefault-handling function · cb7a7055
      Kirill Smelkov authored
      Non on-pagefault code should not access any not-mmapped memory.
      
      Here we just refactor the code we already had to block/restore
      SIGSEGV from fileh_dirty_writeout() and use it in all functions called
      from non-pagefaulting context, as promised.
      
      This way, if there is an error in virtmem implementation which
      incorrectly accesses prepared for BigFile maps memory, we'll just die
      with coredump instead of trying to incorrectly handle the pagefault.
      cb7a7055
  9. 26 Jun, 2015 1 commit
    • Kirill Smelkov's avatar
      tests: Allow to test with ZEO & NEO ZODB storages · 7fc4ec66
      Kirill Smelkov authored
      Previously we were always testing with DBs backed up by FileStorage. Now
      we provide a way to run the testsuite with user selected storage
      backend:
      
          $ WENDELIN_CORE_TEST_DB="<fs>"   make test.py     # test with temporary db with FileStorage
          $ WENDELIN_CORE_TEST_DB="<zeo>"  make test.py     # ----------//---------- with ZEO
          $ WENDELIN_CORE_TEST_DB="<neo>"  make test.py     # ----------//---------- with NEO
      
          $ WENDELIN_CORE_TEST_DB=neo://db@master  make test.py     # test with externally provided DB
      
      Default is still to run tests with FileStorage.
      
      /cc @jm
      7fc4ec66
  10. 25 Jun, 2015 2 commits
  11. 02 Jun, 2015 3 commits
  12. 29 Apr, 2015 1 commit
    • Kirill Smelkov's avatar
      bigfile/ram_shmfs: Try to support compiling on older systems · 48defba0
      Kirill Smelkov authored
      For example on Debian 7 Wheezy the kernel (linux 3.2) supports holepunching,
      because holepunching was added in linux 2.6.38:
      
          http://git.kernel.org/linus/79124f18b335172e1916075c633745e12dae1dac
      
      but glibc does not provide fallocate mode constants, and compilation fails:
      
          gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -D_GNU_SOURCE -I./include -I./3rdparty/ccan -I./3rdparty/include -I/opt/slapgrid/f2a5f59d0d2b521681b9333ee76a2859/parts/python2.7/include/python2.7 -c bigfile/ram_shmfs.c -o build/temp.linux-x86_64-2.7/bigfile/ram_shmfs.o -std=gnu99 -fplan9-extensions -fvisibility=hidden -Wno-declaration-after-statement -Wno-error=declaration-after-statement
          bigfile/ram_shmfs.c: In function ‘shmfs_drop_memory’:
          bigfile/ram_shmfs.c:135:31: error: ‘FALLOC_FL_PUNCH_HOLE’ undeclared (first use in this function)
          bigfile/ram_shmfs.c:135:31: note: each undeclared identifier is reported only once for each function it appears in
          bigfile/ram_shmfs.c:135:54: error: ‘FALLOC_FL_KEEP_SIZE’ undeclared (first use in this function)
          error: command 'gcc' failed with exit status 1
      
      Try to fix it via fallbacking to provide the needed defines on older systems ourselves.
      
      Cc: Klaus Wölfel <klaus@nexedi.com>
      Cc: Ivan Tyagov <ivan@tyagov.com>
      48defba0
  13. 03 Apr, 2015 11 commits
    • Kirill Smelkov's avatar
      bigfile: Basic benchmarks · bb9d8bf1
      Kirill Smelkov authored
          - for virtual memory subsytem
          - for ZBigFiles
      
      They are not currently great, e.g. for virtmem we have in-kernel
      overhead of page clearing - in perf profiles, for bigfile_mmap compared
      to file_read kernel's clear_page_c raises significantly.
      
      That is the worker for clearing page memory and we currently cannot
      avoid that - any memory obtained from kernel (MAP_ANONYMOUS, mmap(file)
      with hole, etc...) comes pre-initialized to zeros to userspace.
      
      This can be seen in the benchmarks as well: file_readbig differs from
      file_read in only that the latter uses 1 small buffer and the first
      allocates large memory (cleared by kernel + python does the memset).
      
          bigfile/tests/bench_virtmem.py@125::bench_file_mmap_adler32     0.47  (0.86 0.49 0.47)
          bigfile/tests/bench_virtmem.py@126::bench_file_read_adler32     0.69  (1.11 0.71 0.69)
          bigfile/tests/bench_virtmem.py@127::bench_file_readbig_adler32  1.41  (1.70 1.42 1.41)
          bigfile/tests/bench_virtmem.py@128::bench_bigfile_mmap_adler32  1.42  (1.45 1.42 1.51)
      
          bigfile/tests/bench_virtmem.py@130::bench_file_mmap_md5         1.52  (1.91 1.54 1.52)
          bigfile/tests/bench_virtmem.py@131::bench_file_read_md5         1.73  (2.10 1.75 1.73)
          bigfile/tests/bench_virtmem.py@132::bench_file_readbig_md5      2.44  (2.73 2.46 2.44)
          bigfile/tests/bench_virtmem.py@133::bench_bigfile_mmap_md5      2.40  (2.48 2.40 2.53)
      
      There is MAP_UNINITIALIZED which works only for non-mmu targets and only
      if explicitly allowed when configuring kernel (off by default).
      
      There were patches to disable that pages zeroing, as it gives
      significant speedup for people's workloads, e.g. [1,2] but all of them
      did not got merged for security reasons.
      
      [1] http://marc.info/?t=132691315900001&r=1&w=2
      [2] http://thread.gmane.org/gmane.linux.kernel/548926
      
      ~~~~
      
      For ZBigFile - it is the storage who is dominating in profiles.
      bb9d8bf1
    • Kirill Smelkov's avatar
      bigfile: BigFile backend to store data in ZODB · 4174b84a
      Kirill Smelkov authored
      This adds transactionality and with e.g. NEO[1] allows to distribute
      objects to nodes into cluster.
      
      We hook into ZODB two-phase commit process as a separate data manager,
      and synchronize changes to memory, to changes to object only at that
      time.
      
      Alternative would be to get notified on every page change, and mark
      appropriate object as dirty right at that moment.
      
      But I wanted to stay close to filesystem design (we don't get
      notification for every file change from kernel) - that's why it is done
      the first way.
      
      [1] http://www.neoppod.org/
      4174b84a
    • Kirill Smelkov's avatar
      bigfile: BackFile backend to store data in an OS file · b3910de8
      Kirill Smelkov authored
      In Python. To demonstrate how backends could be implemented and test
      system on simple things.
      b3910de8
    • Kirill Smelkov's avatar
      bigfile: Python wrapper around virtual memory subsystem · 35eb95c2
      Kirill Smelkov authored
      Exposes BigFile - this way users can define BigFile backend in Python.
      
      Also exposed are BigFile handles, and VMA objects which are results of
      mmaping.
      35eb95c2
    • Kirill Smelkov's avatar
      bigfile/virtmem: Userspace Virtual Memory Manager · 9a293c2d
      Kirill Smelkov authored
      Does similar things to what kernel does - users can mmap file parts into
      address space and access them read/write. The manager will be getting
      invoked by hardware/OS kernel for cases when there is no page loaded for
      read, or when a previousle read-only page is being written to.
      
      Additionally to features provided in kernel, it support to be used to
      store back changes in transactional way (see fileh_dirty_writeout()) and
      potentially use huge pages for mappings (though this is currently TODO)
      9a293c2d
    • Kirill Smelkov's avatar
      bigfile: RAM subsystem · 8c935a5f
      Kirill Smelkov authored
      This thing allows to get aliasable RAM from OS kernel and to manage it.
      Currently we get memory from a tmpfs mount, and hugetlbfs should also
      work, but is TODO because hugetlbfs in the kernel needs to be improved.
      
      We need aliasing because we'll need to be able to memory map the same
      page into several places in address space, e.g. for taking two slices
      overlapping slice of the same array at different times.
      
      Comes with test programs that show we aliasing does not work for
      anonymous memory.
      8c935a5f
    • Kirill Smelkov's avatar
      bigfile: Stub for virtmem · 77d61533
      Kirill Smelkov authored
      This will be the core of virtual memory subsystem. For now we just
      define a structure to describe pages of memory and add utility to
      allocate address space from OS.
      77d61533
    • Kirill Smelkov's avatar
      Low-level pagefault handler · 6f7d4d64
      Kirill Smelkov authored
      We hook into SIGSEGV and handle read/write pagefaults this way.
      
      In this patch there goes stub code that only detects faults and
      determines (in arch specific way) whether fault was for read or write
      and there is a TODO to pass that information to higher level.
      
      It also comes with tests to detect we still crash if we access something
      incorrectly, so people could have coredumps and investigate them.
      6f7d4d64
    • Kirill Smelkov's avatar
      bigfile/pagemap: specialized {} uint64 -> void * mapping · 45af76e6
      Kirill Smelkov authored
      For BigFiles we'll needs to maintain `{} offset-in-file -> void *` mapping. A
      hash or a binary tree could be used there, but since we know files are
      most of the time accessed sequentially and locally in pages-batches, we
      can also organize the mapping in batches of keys.
      
      Specifically offset bits are so divided into parts, that every part
      addresses 1 entry in a table of hardware-page in size. To get to the actual
      value, the system lookups first table by first part of offset, then from
      first table and next part from address - second table, etc.
      
      To clients this looks like a dictionary with get/set/del & clear methods,
      but lookups are O(1) time always, and in contrast to hashes values are
      stored with locality (= adjacent lookups almost always access the same tables).
      45af76e6
    • Kirill Smelkov's avatar
      Basic setup.py / Makefile to build/install/sdist stuff + bigfile.so skeleton · 5755a6b3
      Kirill Smelkov authored
      It is an early project decision to use gnu99 & Plan9 C extensions, to
      simplify C code.
      
      So far we only build stub wendelin/bigfile/_bigfile.so .
      
      Makefile is introduced because there will be targets which are easier to
      handle at make level. For end users no make knowledge is required -
      usual `python setup.py build|install|...` work, redirecting to make
      where necessary.
      
      As was promised (e870781d "Top-level in-tree import redirector")
      setup.py contains install-time hooks to handle in-tree wendelin.py and
      install it as a module namespace.
      
      For sdist, we just use `git ls-files` info if we are in a checkout.
      5755a6b3
    • Kirill Smelkov's avatar
      Start wendelin.bigfile module · 1e92c921
      Kirill Smelkov authored
      This will be a module to allow users to program custom file-like
      backends and latter memory-map content of files from the backend.
      
      For now just start the module structure.
      1e92c921