Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Commit

Permalink
For #12711 - Use a separate reader for autocomplete suggestions
Browse files Browse the repository at this point in the history
Decouple this functionality from search suggestions allowing for individual
control over the readers used.

(cherry picked from commit 2026625)

# Conflicts resolved:
#	docs/changelog.md
  • Loading branch information
Mugurell authored and mergify[bot] committed Sep 5, 2022
1 parent af8121c commit a662769
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,26 @@ const val DB_NAME = "places.sqlite"
internal interface Connection : Closeable {
fun registerWithSyncManager()

/**
* Allows to read history, bookmarks, and other data from persistent storage.
* All calls on the same reader are queued. Multiple readers are allowed.
*/
fun reader(): PlacesReaderConnection

/**
* Create a new reader for history, bookmarks and other data from persistent storage.
* Allows for disbanded calls from the default reader from [reader] and so being able to
* easily start and cancel any data requests without impacting others using another reader.
*
* All [PlacesApi] requests are synchronized at lower levels so even with using multiple readers
* all requests are ordered with concurrent reads not possible.
*/
fun newReader(): PlacesReaderConnection

/**
* Allowed to add history, bookmarks and other data to persistent storage.
* All calls are queued and synchronized at lower levels. Only one writer is recommended.
*/
fun writer(): PlacesWriterConnection

// Until we get a real SyncManager in application-services libraries, we'll have to live with this
Expand Down Expand Up @@ -87,6 +106,12 @@ internal object RustPlacesConnection : Connection {
return cachedReader!!
}

override fun newReader(): PlacesReaderConnection = synchronized(this) {
val api = safeGetApi()
check(api != null) { "must call init first" }
return api.openReader()
}

override fun writer(): PlacesWriterConnection {
val api = safeGetApi()
check(api != null) { "must call init first" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ package mozilla.components.browser.storage.sync

import android.content.Context
import android.net.Uri
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.withContext
import mozilla.appservices.places.PlacesApi
import mozilla.appservices.places.PlacesReaderConnection
import mozilla.appservices.places.uniffi.PlacesException
import mozilla.appservices.places.uniffi.VisitObservation
import mozilla.components.concept.base.crash.CrashReporting
Expand Down Expand Up @@ -44,6 +46,12 @@ open class PlacesHistoryStorage(
context: Context,
crashReporter: CrashReporting? = null
) : PlacesStorage(context, crashReporter), HistoryStorage, HistoryMetadataStorage, SyncableStore {
/**
* Separate reader used only for autocomplete suggestions allowing to decouple this functionality
* from the history suggestions feature and independent reader management.
*/
@VisibleForTesting
internal val autocompleteReader: PlacesReaderConnection by lazy { places.newReader() }

override val logger = Logger("PlacesHistoryStorage")

Expand Down Expand Up @@ -151,7 +159,8 @@ open class PlacesHistoryStorage(

override fun getAutocompleteSuggestion(query: String): HistoryAutocompleteResult? {
val url = handlePlacesExceptions("getAutoCompleteSuggestions", default = null) {
places.reader().matchUrl(query)
autocompleteReader.interrupt()
autocompleteReader.matchUrl(query)
} ?: return null

val resultText = segmentAwareDomainMatch(query, arrayListOf(url))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import org.json.JSONObject
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
Expand All @@ -39,6 +40,7 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import java.io.File
Expand Down Expand Up @@ -456,6 +458,20 @@ class PlacesHistoryStorageTest {
assertNull(history.getAutocompleteSuggestion("hello"))
}

@Test
fun `store uses a different reader for autocomplete suggestions`() {
val connection: RustPlacesConnection = mock()
doReturn(mock<PlacesReaderConnection>()).`when`(connection).reader()
doReturn(mock<PlacesReaderConnection>()).`when`(connection).newReader()
val storage = MockingPlacesHistoryStorage(connection)

storage.getAutocompleteSuggestion("Test")

assertNotEquals(storage.reader, storage.autocompleteReader)
verify(storage.autocompleteReader).interrupt()
verify(storage.autocompleteReader).matchUrl("Test")
}

@Test
fun `store ignores url parse exceptions during record operations`() = runTestOnMain {
// These aren't valid URIs, and if we're not explicitly ignoring exceptions from the underlying
Expand Down Expand Up @@ -602,6 +618,11 @@ class PlacesHistoryStorageTest {
return mock()
}

override fun newReader(): PlacesReaderConnection {
fail()
return mock()
}

override fun writer(): PlacesWriterConnection {
fail()
return mock()
Expand Down Expand Up @@ -658,6 +679,11 @@ class PlacesHistoryStorageTest {
return mock()
}

override fun newReader(): PlacesReaderConnection {
fail()
return mock()
}

override fun writer(): PlacesWriterConnection {
fail()
return mock()
Expand Down Expand Up @@ -706,6 +732,11 @@ class PlacesHistoryStorageTest {
return mock()
}

override fun newReader(): PlacesReaderConnection {
fail()
return mock()
}

override fun writer(): PlacesWriterConnection {
fail()
return mock()
Expand Down Expand Up @@ -761,6 +792,11 @@ class PlacesHistoryStorageTest {
return mock()
}

override fun newReader(): PlacesReaderConnection {
fail()
return mock()
}

override fun writer(): PlacesWriterConnection {
fail()
return mock()
Expand Down
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ permalink: /changelog/
* [Gecko](https://github.com/mozilla-mobile/android-components/blob/main/buildSrc/src/main/java/Gecko.kt)
* [Configuration](https://github.com/mozilla-mobile/android-components/blob/main/.config.yml)

* **browser-storage-sync**:
* 🚒 Bug fixed [issue #12689](https://github.com/mozilla-mobile/android-components/issues/12689) Decouple autocomplete suggestions from history search suggestions by using a separate reader which allows for separate management.

* **browser-storage-sync**:
* Stop reporting to the crash servers the expected `OperationInterrupted` exceptions for when interrupting in progress reads/writes from Application-Services. [#12557](https://github.com/mozilla-mobile/android-components/issues/12557), [#12569](https://github.com/mozilla-mobile/android-components/issues/12569).

Expand Down

1 comment on commit a662769

@firefoxci-taskcluster
Copy link

Choose a reason for hiding this comment

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

Uh oh! Looks like an error! Details

Client ID static/taskcluster/github does not have sufficient scopes and is missing the following scopes:

{
  "AnyOf": [
    "queue:rerun-task:mobile-level-3/fBbtR1WlQx-Q_iJ6IHAlmQ/H0grJRgWTnqNfuJWoq_97A",
    "queue:rerun-task-in-project:none",
    {
      "AllOf": [
        "queue:rerun-task",
        "assume:scheduler-id:mobile-level-3/fBbtR1WlQx-Q_iJ6IHAlmQ"
      ]
    }
  ]
}

This request requires the client to satisfy the following scope expression:

{
  "AnyOf": [
    "queue:rerun-task:mobile-level-3/fBbtR1WlQx-Q_iJ6IHAlmQ/H0grJRgWTnqNfuJWoq_97A",
    "queue:rerun-task-in-project:none",
    {
      "AllOf": [
        "queue:rerun-task",
        "assume:scheduler-id:mobile-level-3/fBbtR1WlQx-Q_iJ6IHAlmQ"
      ]
    }
  ]
}

  • method: rerunTask
  • errorCode: InsufficientScopes
  • statusCode: 403
  • time: 2022-09-05T13:41:01.583Z

Please sign in to comment.