@Override
    public void commit() {
      if (!fullTx) throw new UnsupportedOperationException("read-only");

      commitLock.writeLock().lock();
      try {
        if (closed) return;
        if (uncommitedData) throw new IllegalAccessError("uncomitted data");
        txs.remove(ref);
        cleanTxQueue();

        if (pojo.hasUnsavedChanges()) pojo.save(this);

        // check no other TX has modified our data
        LongMap.LongMapIterator oldIter = old.longMapIterator();
        while (oldIter.moveToNext()) {
          long recid = oldIter.key();
          for (Reference<Tx> ref2 : txs) {
            Tx tx = ref2.get();
            if (tx == this || tx == null) continue;
            if (tx.mod.containsKey(recid)) {
              close();
              throw new TxRollbackException();
            }
          }
        }

        LongMap.LongMapIterator<Fun.Tuple2> iter = mod.longMapIterator();
        while (iter.moveToNext()) {
          long recid = iter.key();
          if (old.containsKey(recid)) {
            close();
            throw new TxRollbackException();
          }
        }

        iter = mod.longMapIterator();
        while (iter.moveToNext()) {
          long recid = iter.key();

          Fun.Tuple2 val = iter.value();
          Serializer ser = (Serializer) val.b;
          Object old = superGet(recid, ser);
          if (old == null) old = TOMBSTONE;
          for (Reference<Tx> txr : txs) {
            Tx tx = txr.get();
            if (tx == null || tx == this) continue;
            tx.old.putIfAbsent(recid, old);
          }

          if (val.a == TOMBSTONE) {
            superDelete(recid, ser);
          } else {
            superUpdate(recid, val.a, ser);
          }
        }

        // there are no conflicts, so update the POJO in parent
        // TODO sort of hack, is it thread safe?
        getWrappedEngine().getSerializerPojo().registered = pojo.registered;
        superCommit();

        close();
      } finally {
        commitLock.writeLock().unlock();
      }
    }