SPage last = null;
      while (last == null || last.children != null) {
        last = findChildTail(last);
        //TODO: do this lazily
        TupleBatch batch = last.getValues();
        places.add(new SearchResult(-batch.getTuples().size() -1, last, batch));
      }
    } else {
      match = find(tuple, places);
      if (match != null) {
        if (mode != InsertMode.UPDATE) {
          return match;
        }
        SearchResult last = places.getLast();
        SPage page = last.page;
        last.values.getTuples().set(last.index, tuple);
        page.setValues(last.values);
        return match;
      }
    }
    List key = extractKey(tuple);
    int level = 0;
    if (mode != InsertMode.ORDERED) {
      if (sizeHint > -1) {
        level = Math.min(sizeHint, randomLevel());
      } else {
        level = randomLevel();
      }
    } else if (!places.isEmpty() && places.getLast().values.getTuples().size() == pageSize) {
      int row = rowCount.get();
      while (row != 0 && row%pageSize == 0) {
        row = (row - pageSize + 1)/pageSize;
        level++;
      }
    }
    assert header.length == places.size();
    if (level >= header.length) {
      header = Arrays.copyOf(header, level + 1);
    }
    rowCount.addAndGet(1);
    SPage page = null;
    for (int i = 0; i <= level; i++) {
      if (places.isEmpty()) {
        SPage newHead = new SPage(this, false);
        TupleBatch batch = newHead.getValues();
        batch.getTuples().add(key);
        newHead.setValues(batch);
        newHead.children.add(page);
        header[i] = newHead;
        page = newHead;
      } else {
        SearchResult result = places.removeLast();
        Object value = (i == 0 ? tuple : page);
        page = insert(key, result, places.peekLast(), value, mode == InsertMode.ORDERED);
      }
    }
    return null;