| 794 | } |
| 795 | void remove() { db.checkError("BtreeDelete", sqlite3BtreeDelete(cursor)); } |
| 796 | void set(KeyValueRef kv) { |
| 797 | if (db.fragment_values) { |
| 798 | // Unlike a read, where we need to access fragments in fully forward or reverse order, |
| 799 | // here we just want to delete any existing fragments for the key. It does not matter |
| 800 | // what order we delete them in, and SQLite requires us to seek after every delete, so |
| 801 | // the fastest way to do this is to repeatedly seek to the tuple prefix (key, ) and |
| 802 | // delete the current fragment until nothing is there. |
| 803 | // This should result in almost identical performance to non-fragmenting mode for single fragment kv pairs. |
| 804 | int seekResult = moveTo(kv.key, true); // second arg means to ignore fragmenting and seek to (key, ) |
| 805 | while (seekResult == 0) { |
| 806 | remove(); |
| 807 | seekResult = moveTo(kv.key, true); |
| 808 | } |
| 809 | |
| 810 | const int primaryPageUsable = SERVER_KNOBS->SQLITE_FRAGMENT_PRIMARY_PAGE_USABLE; |
| 811 | const int overflowPageUsable = SERVER_KNOBS->SQLITE_FRAGMENT_OVERFLOW_PAGE_USABLE; |
| 812 | |
| 813 | int fragments = 1; |
| 814 | int valuePerFragment = kv.value.size(); |
| 815 | |
| 816 | // Figure out if we would benefit from fragmenting this kv pair. The key size must be less than |
| 817 | // primary page usable size, and the value and key size together must exceeed the primary page usable size. |
| 818 | if ((kv.key.size() + kv.value.size()) > primaryPageUsable && kv.key.size() < primaryPageUsable) { |
| 819 | |
| 820 | // Just the part of the value that would be in a partially-filled overflow page |
| 821 | int overflowPartialBytes = (kv.expectedSize() - primaryPageUsable) % overflowPageUsable; |
| 822 | |
| 823 | // Number of bytes wasted in the unfragmented case |
| 824 | int unfragmentedWaste = overflowPageUsable - overflowPartialBytes; |
| 825 | |
| 826 | // Total space used for unfragmented form |
| 827 | int unfragmentedTotal = kv.expectedSize() + unfragmentedWaste; |
| 828 | |
| 829 | // Value bytes that can fit in the primary page for each fragment |
| 830 | int primaryPageValueBytes = primaryPageUsable - kv.key.size(); |
| 831 | |
| 832 | // Calculate how many total fragments it would take to spread the partial overflow page bytes and the |
| 833 | // first fragment's primary page value bytes evenly over multiple tuples that fit in primary pages. |
| 834 | fragments = |
| 835 | (primaryPageValueBytes + overflowPartialBytes + primaryPageValueBytes - 1) / primaryPageValueBytes; |
| 836 | |
| 837 | // Number of bytes wasted in the fragmented case (for the extra key copies) |
| 838 | int fragmentedWaste = kv.key.size() * (fragments - 1); |
| 839 | |
| 840 | // Total bytes used for the fragmented case |
| 841 | // int fragmentedTotal = kv.expectedSize() + fragmentedWaste; |
| 842 | |
| 843 | // Calculate bytes saved by having extra key instances stored vs the original partial overflow page |
| 844 | // bytes. |
| 845 | int savings = unfragmentedWaste - fragmentedWaste; |
| 846 | |
| 847 | double reduction = (double)savings / unfragmentedTotal; |
| 848 | |
| 849 | // printf("K: %5d V: %6d OVERFLOW: %5d FRAGMENTS: %3d SAVINGS: %4d FRAG: %7d UNFRAG: %7d |
| 850 | // REDUCTION: %.3f\n", kv.key.size(), kv.value.size(), overflowPartialBytes, fragments, savings, |
| 851 | // fragmentedTotal, unfragmentedTotal, reduction); |
| 852 | if (reduction < SERVER_KNOBS->SQLITE_FRAGMENT_MIN_SAVINGS) |
| 853 | fragments = 1; |
nothing calls this directly
no test coverage detected