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

android: create persistent SharedPreferences-based KV store #2319

Merged
merged 13 commits into from
Jun 8, 2022

Conversation

goaway
Copy link
Contributor

@goaway goaway commented May 25, 2022

Description: Updates the exposed KeyValueStore type to be a more traditional implementable interface, and provides a simple persisting implementation based on Android SharedPreferences.
Risk: Low
Testing: Application

Signed-off-by: Mike Schore mike.schore@gmail.com

Signed-off-by: Mike Schore <mike.schore@gmail.com>
Signed-off-by: Mike Schore <mike.schore@gmail.com>
@goaway goaway marked this pull request as ready for review May 27, 2022 17:55
@jpsim
Copy link
Contributor

jpsim commented May 27, 2022

Looks straightforward enough. Should we expose a builder API to enable this?

Signed-off-by: Mike Schore <mike.schore@gmail.com>
@goaway
Copy link
Contributor Author

goaway commented Jun 1, 2022

@jpsim Setting a KV store is already supported in the Kotlin EngineBuilder, with Swift to follow. :)


import io.envoyproxy.envoymobile.KeyValueStore

class SharedPreferencesStore(sharedPreferences: SharedPreferences) : KeyValueStore {
Copy link
Contributor

Choose a reason for hiding this comment

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

should we have some docs for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added docstring, and the interface is documented - would be great to generate docs from these.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(Looks like @jpsim has made some great progress here!)


import io.envoyproxy.envoymobile.KeyValueStore

class SharedPreferencesStore(sharedPreferences: SharedPreferences) : KeyValueStore {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add some unit tests specific to SharedPreferencesStore class?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We don't today have a unit test setup that loads the Android platform APIs, because we don't today have a lot of Android-only or Android-specific code (by design). If/as we add more, we probably do want such a test suite, but this is mostly meant to be a trivial example implementation that guides others in using the API. That said, once we have a place for such tests to live, I agree we should cover this at the unit level for completeness' sake.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, thank you for the context.

I would argue that we should have a test even for this trivial example since otherwise we cannot really tell whether it works or not the way we expect it to work - as in, I think that we are past the threshold when we can justify having an Android tests suite (if we have a need for Android code we have a need for Android tests).

Could we create a GH issue for this if you do not want to do this as part of this PR? I would love us to start investing in testing more, otherwise it's going to become increasingly hard to continue to move forward without leaving broken stuff behind us.

Copy link
Contributor

Choose a reason for hiding this comment

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

Roboelectric supports this today, and we use it in quite a few tests: https://github.com/envoyproxy/envoy-mobile/search?q=robolectric

Signed-off-by: Mike Schore <mike.schore@gmail.com>
@jpsim
Copy link
Contributor

jpsim commented Jun 2, 2022

Should we expose a builder API to enable this?

Setting a KV store is already supported in the Kotlin EngineBuilder

Sorry I wasn't clear. Right now to use this I think users would have to configure the builder like this:

.addKeyValueStore("some name the user has to choose", SharedPreferencesStore(...))

Why do we have to expose the SharedPreferencesStore in the public API at all? Could we not do this?

.useDefaultPersistentKeyValueStore(true)

And configure SharedPreferencesStore entirely internally to the library?

@goaway
Copy link
Contributor Author

goaway commented Jun 2, 2022

@jpsim a name for the store is required in the builder API, because more than one key-value store may be registered. The SharedPreferencesStore implementation requires an instance of SharedPreferences which is create/provided by the Android API. We could create our own, but that would then limit the user's ability to supply one of their choosing. (There are also limitations to creating our own - it would require even more general access to the application context, of which EM is currently agnostic.)

https://developer.android.com/training/data-storage/shared-preferences

Anyways, that's the reasoning behind the method signatures. :)

@goaway
Copy link
Contributor Author

goaway commented Jun 2, 2022

As an update to my comment above, I misremembered - EM does have access to some derivation of the application context at least at creation, since that's needed to initialize DNS. So we could in theory use that to create our own default SharedPreferences instance. But I still have some concerns: we don't really know much about that context and whether it's appropriate to use it for that purpose (there's several flavors of Android context and we're not picky about which we receive), we don't really have any notion of a reserved namespace for creating our store, there's a potential for leakiness in usage of a persistent store if it's not somehow managed, and finally I'm just not certain of what the right defaults should be.

So my inclination is to say that I while think this is a good idea, we perhaps ought to opt for simplicity and flexibility at this point until we have a better notion of where and how this gets used.

@jpsim
Copy link
Contributor

jpsim commented Jun 2, 2022

a name for the store is required in the builder API, because more than one key-value store may be registered

I understand that, but we could have a reserved/default name for this persistent store.

Noted on the complexity and responsibility creep involved in accessing the application context internally in Envoy Mobile. Requiring this to be wired explicitly is fine with me for now, but ideally we'd improve on this pattern so that Envoy Mobile's default configuration gets this benefit out of the box.

Also, having reasonable/good defaults doesn't mean that we shouldn't expose ways to change them in case they want to refine/disable/change it for their use case.

@Augustyniak
Copy link
Contributor

Also, having reasonable/good defaults doesn't mean that we shouldn't expose ways to change them in case they want to refine/disable/change it for their use case.

That would be my preference as well -> make some defaults that work just fine for most users and allow them to override it when they need something custom. System level networking libraries on iOS/Android do not require you to provide any caching configuration to get basic caching up and running.

If we want to experiment with this first that's fine but I would argue that the final API needs to have some notions of defaults implemented.

@goaway
Copy link
Contributor Author

goaway commented Jun 2, 2022

I think we're all in agreement here? The only thing I'm proposing is we hold off on making assumptions around what a default should be until we understand see a bit better how it's being used (alt-svc caching, http caching, etc.).

@jpsim
Copy link
Contributor

jpsim commented Jun 3, 2022

I think we're all in agreement here?

To recap some offline discussions, yes in agreement as a first version, which requires consumers to wire this through the addKeyValueStore builder API.

Ideally before merging this would still have the following:

  1. changelog entry
  2. doc updates
  3. usage in the experimental Kotlin app

I still think a test using roboelectric to simulate Android's shared preferences would be good to have but understand if that's not something you want to tackle here.

Signed-off-by: Mike Schore <mike.schore@gmail.com>
Signed-off-by: Mike Schore <mike.schore@gmail.com>
Signed-off-by: Mike Schore <mike.schore@gmail.com>
Signed-off-by: Mike Schore <mike.schore@gmail.com>
Augustyniak
Augustyniak previously approved these changes Jun 3, 2022
jpsim
jpsim previously approved these changes Jun 3, 2022
Copy link
Contributor

@jpsim jpsim left a comment

Choose a reason for hiding this comment

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

Nice work

Signed-off-by: Mike Schore <mike.schore@gmail.com>
@goaway goaway dismissed stale reviews from jpsim and Augustyniak via f2db4bc June 3, 2022 20:49
Signed-off-by: Mike Schore <mike.schore@gmail.com>
Signed-off-by: Mike Schore <mike.schore@gmail.com>
Augustyniak
Augustyniak previously approved these changes Jun 6, 2022
Signed-off-by: Mike Schore <mike.schore@gmail.com>
Signed-off-by: Mike Schore <mike.schore@gmail.com>
@goaway goaway merged commit 9d7d984 into main Jun 8, 2022
@goaway goaway deleted the ms/default-android-store branch June 8, 2022 08:21
jpsim added a commit that referenced this pull request Jun 14, 2022
* origin/main: (33 commits)
  iOS: fix xcframework upload in release workflow (#2366)
  config: hopefully fixing C++ config default for apple (#2355)
  Update Envoy (#2364)
  Bump Lyft Support Rotation (#2365)
  ci: pin external GitHub Action (#2363)
  cleanup: fix warning in JNI layer (#2361)
  cleanup: convert some more uses of NULL to nullptr (#2359)
  cleanup: consistently use nullptr in cc contexts (#2351)
  cleanup: remove unused function and resolve warning (#2350)
  iOS: add configurable gzip and brotli decompression options (#2349)
  iOS: stop embedding bitcode in releases (#2347)
  ci: update Android setup (#2354)
  docs: update the list of clusters (#2344)
  bazel: update rules_apple (#2346)
  iOS: add a way to disable network monitoring (#2345)
  api: adding brotli knobs (#2342)
  android: create persistent SharedPreferences-based KV store (#2319)
  ios: add support for registering a platform KV store (#2334)
  builder: making compressor configurable (#2321)
  iOS: add SwiftPM example (#2333)
  ...

Signed-off-by: JP Simard <jp@jpsim.com>
jpsim added a commit that referenced this pull request Jun 14, 2022
* origin/main: (33 commits)
  iOS: fix xcframework upload in release workflow (#2366)
  config: hopefully fixing C++ config default for apple (#2355)
  Update Envoy (#2364)
  Bump Lyft Support Rotation (#2365)
  ci: pin external GitHub Action (#2363)
  cleanup: fix warning in JNI layer (#2361)
  cleanup: convert some more uses of NULL to nullptr (#2359)
  cleanup: consistently use nullptr in cc contexts (#2351)
  cleanup: remove unused function and resolve warning (#2350)
  iOS: add configurable gzip and brotli decompression options (#2349)
  iOS: stop embedding bitcode in releases (#2347)
  ci: update Android setup (#2354)
  docs: update the list of clusters (#2344)
  bazel: update rules_apple (#2346)
  iOS: add a way to disable network monitoring (#2345)
  api: adding brotli knobs (#2342)
  android: create persistent SharedPreferences-based KV store (#2319)
  ios: add support for registering a platform KV store (#2334)
  builder: making compressor configurable (#2321)
  iOS: add SwiftPM example (#2333)
  ...

Signed-off-by: JP Simard <jp@jpsim.com>
Augustyniak pushed a commit that referenced this pull request Jun 28, 2022
Description: Updates the exposed KeyValueStore type to be a more traditional implementable interface, and provides a simple persisting implementation based on Android SharedPreferences.
Risk: Low
Testing: Application

Signed-off-by: Mike Schore <mike.schore@gmail.com>
Signed-off-by: Rafal Augustyniak <raugustyniak@lyft.com>
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.

None yet

3 participants