RESOLVED FIXED211929
ITP database should finalize all prepared statements before closing
https://bugs.webkit.org/show_bug.cgi?id=211929
Summary ITP database should finalize all prepared statements before closing
Kate Cheney
Reported 2020-05-14 15:57:41 PDT
If the database connection is associated with unfinalized prepared statements or unfinished sqlite3_backup objects then sqlite3_close() will leave the database connection open and return SQLITE_BUSY (https://www.sqlite.org/c3ref/close.html).
Attachments
Patch (9.27 KB, patch)
2020-05-14 17:13 PDT, Kate Cheney
no flags
Patch (94.20 KB, patch)
2020-05-18 09:10 PDT, Kate Cheney
no flags
Patch (79.86 KB, patch)
2020-05-18 17:11 PDT, Kate Cheney
no flags
Patch (79.83 KB, patch)
2020-05-19 11:13 PDT, Kate Cheney
no flags
Patch (79.86 KB, patch)
2020-05-19 13:09 PDT, Kate Cheney
no flags
Patch for landing (79.97 KB, patch)
2020-05-19 15:35 PDT, Kate Cheney
no flags
Kate Cheney
Comment 1 2020-05-14 15:59:51 PDT
Kate Cheney
Comment 2 2020-05-14 17:13:18 PDT
Sihui Liu
Comment 3 2020-05-15 11:27:10 PDT
Comment on attachment 399430 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=399430&action=review > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:345 > + ASSERT_NOT_REACHED(); According to https://www.sqlite.org/c3ref/finalize.html, sqlite3_finalize() can return error when the last execution of the statement fails, so maybe it's fine to ignore the error of finalize. > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:656 > +bool ResourceLoadStatisticsDatabaseStore::finalizeStatements() I am not familiar with this code; why not use unique pointer for the statements and initialize them only when a statement is used? In that case, you can null out those statements before closing database. ~SQLiteStatement() will do finalize(). It should be easier to manage the lifetime of the objects.
Kate Cheney
Comment 4 2020-05-18 09:10:02 PDT
Kate Cheney
Comment 5 2020-05-18 09:10:49 PDT
(In reply to Sihui Liu from comment #3) > Comment on attachment 399430 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=399430&action=review > > > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:345 > > + ASSERT_NOT_REACHED(); > > According to https://www.sqlite.org/c3ref/finalize.html, sqlite3_finalize() > can return error when the last execution of the statement fails, so maybe > it's fine to ignore the error of finalize. > > > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:656 > > +bool ResourceLoadStatisticsDatabaseStore::finalizeStatements() > > I am not familiar with this code; why not use unique pointer for the > statements and initialize them only when a statement is used? > In that case, you can null out those statements before closing database. > ~SQLiteStatement() will do finalize(). It should be easier to manage the > lifetime of the objects. Good idea! The most recent patch makes this change.
John Wilander
Comment 6 2020-05-18 15:40:08 PDT
Comment on attachment 399649 [details] Patch Could the init code be in a function on this form: if (!statementParam) { statementParam = makeUnique<WebCore::SQLiteStatement>(m_database, queryParam); if (statementParam->prepare() != SQLITE_OK) { RELEASE_LOG_ERROR(Network, "%p - %s failed to prepare statement, error message: %{private}s", this, logStringParam, m_database.lastErrorMsg()); ASSERT_NOT_REACHED(); return; } }
Kate Cheney
Comment 7 2020-05-18 17:11:04 PDT
Kate Cheney
Comment 8 2020-05-18 17:11:49 PDT
(In reply to John Wilander from comment #6) > Comment on attachment 399649 [details] > Patch > > Could the init code be in a function on this form: > > if (!statementParam) { > statementParam = makeUnique<WebCore::SQLiteStatement>(m_database, > queryParam); > if (statementParam->prepare() != SQLITE_OK) { > RELEASE_LOG_ERROR(Network, "%p - %s failed to prepare statement, error > message: %{private}s", this, logStringParam, m_database.lastErrorMsg()); > ASSERT_NOT_REACHED(); > return; > } > } Yes, this makes this patch much cleaner. Thanks!
Sihui Liu
Comment 9 2020-05-19 09:18:47 PDT
Comment on attachment 399685 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=399685&action=review Generally looks good. > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:510 > + statement = makeUnique<WebCore::SQLiteStatement>(m_database, query); ASSERT(m_database->isOpen()); > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:529 > ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK); I see there are a lot of reset()s after each execution of statement. You can try use SQLiteStatementAutoResetScope. Make initializeStatement(rename it something like getScopedStatement) to return Optional<SQLiteStatementAutoResetScope>. > Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp:257 > + if (m_statisticsStore && !isEphemeral() && is<ResourceLoadStatisticsDatabaseStore>(*m_statisticsStore)) > + downcast<ResourceLoadStatisticsDatabaseStore>(*m_statisticsStore).close(); > + Seems like you can do the close() in ~ResourceLoadStatisticsDatabaseStore. Then you don't need the explicit close().
Kate Cheney
Comment 10 2020-05-19 09:28:09 PDT
(In reply to Sihui Liu from comment #9) > Comment on attachment 399685 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=399685&action=review > > Generally looks good. > Thanks for the feedback, this is super helpful. > > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:510 > > + statement = makeUnique<WebCore::SQLiteStatement>(m_database, query); > > ASSERT(m_database->isOpen()); > Good catch. > > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:529 > > ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK); > > I see there are a lot of reset()s after each execution of statement. You can > try use SQLiteStatementAutoResetScope. Make initializeStatement(rename it > something like getScopedStatement) to return > Optional<SQLiteStatementAutoResetScope>. > I didn't know that existed, great! I assume this means no need to call statement->reset()? > > Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp:257 > > + if (m_statisticsStore && !isEphemeral() && is<ResourceLoadStatisticsDatabaseStore>(*m_statisticsStore)) > > + downcast<ResourceLoadStatisticsDatabaseStore>(*m_statisticsStore).close(); > > + > > Seems like you can do the close() in ~ResourceLoadStatisticsDatabaseStore. > Then you don't need the explicit close(). Yes, good point. I will change this.
Kate Cheney
Comment 11 2020-05-19 11:13:44 PDT
Kate Cheney
Comment 12 2020-05-19 12:51:48 PDT
wincairo does not seem to like SQLiteStatementAutoResetScope
Kate Cheney
Comment 13 2020-05-19 13:09:29 PDT
John Wilander
Comment 14 2020-05-19 14:58:36 PDT
Comment on attachment 399762 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=399762&action=review Given that Sihui has looked this over, I'm OK with it too. See comments though. > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:504 > +SQLiteStatementAutoResetScope ResourceLoadStatisticsDatabaseStore::getScopedStatement(std::unique_ptr<WebCore::SQLiteStatement>& statement, const String query, const String& logString) const Our style guide says no get prefix. If you get a name conflict with a local variable, you can call it with this->scopedStatement(). Can the query parameters be ref? > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:510 > + RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::%s failed to prepare statement, error message: %{private}s", this, logString.ascii().data(), m_database.lastErrorMsg()); I don't remember the reason for keeping these error messages private. Can they leak something about the user's browsing?
Kate Cheney
Comment 15 2020-05-19 15:18:21 PDT
(In reply to John Wilander from comment #14) > Comment on attachment 399762 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=399762&action=review > > Given that Sihui has looked this over, I'm OK with it too. See comments > though. > > > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:504 > > +SQLiteStatementAutoResetScope ResourceLoadStatisticsDatabaseStore::getScopedStatement(std::unique_ptr<WebCore::SQLiteStatement>& statement, const String query, const String& logString) const > > Our style guide says no get prefix. If you get a name conflict with a local > variable, you can call it with this->scopedStatement(). Can the query > parameters be ref? > Yes to ref, I will change. And I'll update the function name. > > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:510 > > + RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::%s failed to prepare statement, error message: %{private}s", this, logString.ascii().data(), m_database.lastErrorMsg()); > > I don't remember the reason for keeping these error messages private. Can > they leak something about the user's browsing? Good point, this can probably be public.
Kate Cheney
Comment 16 2020-05-19 15:20:07 PDT
(In reply to katherine_cheney from comment #15) > (In reply to John Wilander from comment #14) > > Comment on attachment 399762 [details] > > Patch > > > > View in context: > > https://bugs.webkit.org/attachment.cgi?id=399762&action=review > > > > Given that Sihui has looked this over, I'm OK with it too. See comments > > though. > > > > > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:504 > > > +SQLiteStatementAutoResetScope ResourceLoadStatisticsDatabaseStore::getScopedStatement(std::unique_ptr<WebCore::SQLiteStatement>& statement, const String query, const String& logString) const > > > > Our style guide says no get prefix. If you get a name conflict with a local > > variable, you can call it with this->scopedStatement(). Can the query > > parameters be ref? > > > Yes to ref, I will change. And I'll update the function name. > > > > > Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore. > cpp:510 > > > + RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::%s failed to prepare statement, error message: %{private}s", this, logString.ascii().data(), m_database.lastErrorMsg()); > > > > I don't remember the reason for keeping these error messages private. Can > > they leak something about the user's browsing? > > Good point, this can probably be public. Some need to be private because they could potentially leak information about bound values, like domains. But just preparing statements is not leaking anything.
Kate Cheney
Comment 17 2020-05-19 15:35:36 PDT
Created attachment 399774 [details] Patch for landing
EWS
Comment 18 2020-05-19 15:55:53 PDT
Committed r261892: <https://trac.webkit.org/changeset/261892> All reviewed patches have been landed. Closing bug and clearing flags on attachment 399774 [details].
Truitt Savell
Comment 19 2020-05-20 10:12:18 PDT
It looks like the changes here broke multiple API tests. tracking here: https://bugs.webkit.org/show_bug.cgi?id=212153
Kate Cheney
Comment 20 2020-05-20 10:17:58 PDT
(In reply to Truitt Savell from comment #19) > It looks like the changes here > > broke multiple API tests. tracking here: > https://bugs.webkit.org/show_bug.cgi?id=212153 Weird EWS didn't catch this.
Chris Dumez
Comment 21 2020-05-20 10:34:29 PDT
(In reply to katherine_cheney from comment #20) > (In reply to Truitt Savell from comment #19) > > It looks like the changes here > > > > broke multiple API tests. tracking here: > > https://bugs.webkit.org/show_bug.cgi?id=212153 > > Weird EWS didn't catch this. WK2 EWS runs release, not debug :/
Kate Cheney
Comment 22 2020-05-20 10:35:16 PDT
(In reply to Chris Dumez from comment #21) > (In reply to katherine_cheney from comment #20) > > (In reply to Truitt Savell from comment #19) > > > It looks like the changes here > > > > > > broke multiple API tests. tracking here: > > > https://bugs.webkit.org/show_bug.cgi?id=212153 > > > > Weird EWS didn't catch this. > > WK2 EWS runs release, not debug :/ :(
Note You need to log in before you can comment on or make changes to this bug.