Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CBL-6131: Race creating the expiration column in a collection table #2160

Merged
merged 9 commits into from
Oct 25, 2024

Conversation

callumbirks
Copy link
Contributor

Fixed by surrounding the column check with the same file lock used when adding the column.

One can verify that the test Create collection concurrently fails if you revert the lock change.

LiteCore/Storage/SQLiteKeyStore.cc Outdated Show resolved Hide resolved
LiteCore/Storage/SQLiteKeyStore.cc Outdated Show resolved Hide resolved
LiteCore/tests/c4BaseTest.cc Show resolved Hide resolved
@callumbirks
Copy link
Contributor Author

@jianminzhao
I think wrapping the check in a lock is preferable to checking the error, because:
a. The check is guarded by the state _hasExpirationColumn, so this lock should only be entered once by each collection instance.
b. Checking the error would require the error string being correct and never changing. Because the Error has code = SQLITE_ERROR, we have to rely on checking the message string to use that alternative.

@snej
Copy link
Collaborator

snej commented Oct 15, 2024

The fix looks good, I just disagree with the test.

@jianminzhao
Copy link
Contributor

The new version is okay with me: doing an extra mayHaveExpiration inside the transaction, since the positive return is always good to omit the following transaction.

@callumbirks
Copy link
Contributor Author

callumbirks commented Oct 15, 2024

The fix looks good, I just disagree with the test.

@snej I guess you mean as in this is not behaviour we support? I agree, but as this is something that users can do (and now have done), I think its important to test that we keep the logical consistency.

Unless you have something to suggest to change / improve the test?

EDIT: Sorry, just saw the code comment above.

@callumbirks callumbirks requested a review from snej October 17, 2024 09:51
@cbl-bot
Copy link

cbl-bot commented Oct 24, 2024

Code Coverage Results:

Type Percentage
branches 67.91
functions 79.26
instantiations 35.26
lines 79.26
regions 75.15

WithDeletedTable = 500, // Added 'deleted' KeyStore for deleted docs (CBL 3.0?)
WithIndexesLastSeq = 501, // Added 'lastSeq' column to 'indexes' table (CBL 3.2)
MaxReadable = 599, // Cannot open versions newer than this
WithExpirationColumn = 490, // Added 'expiration' column to KeyStore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you insert a new version before existing versions? How would a version at 500 or 501 get upgraded? There should be a test, I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any database at version >=500 already has the expiration column, because addExpiration was added to KeyStore::reopen in the same commit that WithDeletedTable (500) migration was added.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you actually try to retrofit into current upgrade path. This relies on very precise understanding of other part of code and the schema.
Recognizing this fact, we can also treat it as a new version, but because of the above fact, do
ALTER TABLE ..... IF NOT EXISTS
I am little concerned of retrofitting a new version into the history

Copy link
Contributor Author

@callumbirks callumbirks Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ALTER TABLE ..... IF NOT EXISTS

There is no such syntax. We need to check the existence of the column manually

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you check the existence, do we still need to insert a new version into past history? This is the new version, after this new version, we will know schema has the expiration table at the time of creation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the code path that supports your statement? Is it in the same transaction of the upgrade transaction? Is it possible that some failure occurs outside the upgrade transaction that arrives following state:

  • version bumped 500
  • some keystore not opened successfully and expiration not added successfully.
    Since you removed the addExpiration completely, there is no way to have the column.

Copy link
Contributor Author

@callumbirks callumbirks Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQLiteKeyStore::reopen calls addExpiration (on the main branch).
So if a database is version >=500, every collection which has been used since it upgraded to version 500 has the expiration column.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"which has been used since ..." -- what if not used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If not used then I'm not sure

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's worth trying to optimize. Seems like you need to make WithExpiration = 502 and always do it (once) for every database, just to make sure.

@jianminzhao jianminzhao merged commit 6c0e685 into release/3.2 Oct 25, 2024
9 checks passed
@jianminzhao jianminzhao deleted the CBL-6131 branch October 25, 2024 00:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants