Repository navigation
Conversation
ivf_is_trained() kept the trained state per connection once it had read it, and compute-centroids, set-centroid and clear-centroids set that cache as they ran. Another connection's command, or a rolled-back one, left it wrong: a connection that had queried before another trained the index scanned only the emptied unassigned cells and found no rows, and one that had trained it, then saw another connection clear it or rolled its own training back, failed every insert and query with "SQL logic error". The state is now read from _info each time, so it is always that of the transaction reading it.
The IVF commands, and the cell writes behind every insert, ignored the result of most of their statements, so one that failed part way, an interrupt included, left the command or insert to carry on and report success over a half-rebuilt index. compute-centroids and clear-centroids read every vector through ivf_load_all_vectors(), which stopped at a failed step and returned what it had read as all of them, then deleted the cells and rebuilt them from that part, and compute-centroids its _ivf_vectors too. assign-vectors deleted the unassigned cells whatever happened to the vectors it was moving out of them, and ivf_cell_insert() wrote a vector, its rowid and its map entry without checking any of them. Where a failure was reported, it was as "SQL logic error" or "No vectors". compute-centroids also wrapped its work in a SAVEPOINT that never opened, since none can while the INSERT running the command is in progress. Every statement and blob access in the commands, ivf_exec(), ivf_is_trained(), ivf_load_all_vectors() and ivf_cell_insert() now returns its error, and the command or insert returns it to SQLite, which undoes the statement's writes. The savepoint is gone.
An error the command returns is rolled back with the transaction in autocommit mode, or inside BEGIN when it is an interrupt or a memory, I/O or full-disk error. A constraint or other error inside BEGIN leaves the writes before it, since a single-row INSERT into a virtual table opens no statement journal.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a statement inside an IVF command fails, an interrupt included, the command carries on, and it can report success after rebuilding the index from part of its vectors.
The commands, and the cell writes behind every insert, ignored the result of most of their statements:
ivf_load_all_vectors()stopped at a failed step and returned what it had read as all the vectors. compute-centroids and clear-centroids then deleted the cells and rebuilt them from that part, and compute-centroids deleted and rewrote_ivf_vectorsfrom it too.ivf_cell_insert()wrote a vector, its rowid and its rowid-map entry without checking any of the writes, so an insert could report success with its row missing from the index.SAVEPOINT ivf_train, which never opened: SQLite refuses a savepoint while a write statement is in progress ("cannot open savepoint - SQL statements in progress"), and the INSERT running the command is one. ItsROLLBACK TOrestored nothing.Every statement and blob access in the commands,
ivf_exec(),ivf_is_trained(),ivf_load_all_vectors()andivf_cell_insert()now returns its error, and the command or insert returns it. In autocommit mode, and for an interrupt or a memory, I/O or full-disk error anywhere, SQLite then rolls the transaction back with every write the command made. Any other error insideBEGINleaves the writes before it, as it leaves the writes of any failed vec0 statement there: a one-row INSERT into a virtual table opens no statement journal, which is #333. The savepoint is removed.Measured by interrupting one statement at each VM step of a command on 20 rows (with the IVF define):
Here, the clear-centroids and assign-vectors runs that complete despite the interrupt are those where the interrupted statement is the cell lookup in
ivf_cell_find_or_create(), which then opens a new cell: one cell more, no row lost.Test:
test_ivf_command_interrupted_at_each_stepin test-ivf.py interrupts each step of compute-centroids (without a quantizer and with int8), clear-centroids and assign-vectors, and checks that each run reports the interrupt or completes, that every row is still found, and that the command then runs to the end with every row found. All four fail on main and pass here. The Python suite passes (with-DSQLITE_VEC_EXPERIMENTAL_IVF_ENABLE=1, 308 passed, 4 skipped; without it, 239 passed, 73 skipped), under ASan and UBSan as well, and both builds' warnings are main's.This builds on #351 and carries its commit: both change how the commands handle the trained state, and on main they touch adjacent lines. It merges cleanly with every other open PR that touches the IVF code, #347 and #349 to #353 included, but not with #313, which rewrites set-centroid, assign-vectors and clear-centroids to store quantized vectors. The two changes are independent, #313 changing what those commands write and this checking each write, so whichever lands second takes both by hand in those three functions.
Deletes and KNN queries still pass over a failed step, measured the same way: a delete reports success and leaves its row in the index in 25 of 122 runs, and a KNN query on a trained index returns fewer rows than it should, as if they were all, in 52 of 146.
ivf_delete()andivf_query_knn()are changed by #347 and #350, so this leaves them as they are.Origin. Found in the IVF port to photostructure/sqlite-vec, photostructure#12 (65817f4), which lists the ignored results and the savepoint among the port's differences from upstream. The test interrupts a command at each step as mceachen's
test_interrupt_is_reported_as_interruptdoes vec0's statements in photostructure/sqlite-vec@7a9ab11, a test the port extends to IVF in debd171; the change and the test are written here for asg017's main. No open or closed PR or issue here makes the same change.