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

sessionToken Persistence #5941

Merged
merged 18 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
//
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.firebase.firestore.local;

import com.google.protobuf.ByteString;

/**
* General purpose cache for global values for user.
tom-andersen marked this conversation as resolved.
Show resolved Hide resolved
*
* <p>Global state that cuts across components should be saved here. Following are contained herein:
*
* <p>`db_token` tracks server interaction across Listen and Write streams. This facilitates cache
tom-andersen marked this conversation as resolved.
Show resolved Hide resolved
* synchronization and invalidation.
*/
interface GlobalsCache {

ByteString getDbToken();

void setDbToken(ByteString value);

}
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ public final class LocalStore implements BundleCallback {
/** Manages our in-memory or durable persistence. */
private final Persistence persistence;

/** General purpose global state. */
private GlobalsCache globalsCache;

/** Manages the list of active field and collection indices. */
private IndexManager indexManager;

Expand Down Expand Up @@ -153,6 +156,7 @@ public LocalStore(Persistence persistence, QueryEngine queryEngine, User initial
this.persistence = persistence;
this.queryEngine = queryEngine;

globalsCache = persistence.getGlobalsCache();
targetCache = persistence.getTargetCache();
bundleCache = persistence.getBundleCache();
targetIdGenerator = TargetIdGenerator.forTargetCache(targetCache.getHighestTargetId());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
//
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.firebase.firestore.local;

import com.google.protobuf.ByteString;

/** In-memory cache of global values */
final class MemoryGlobalsCache implements GlobalsCache {

private ByteString dbToken;

@Override
public ByteString getDbToken() {
return dbToken;
}

@Override
public void setDbToken(ByteString value) {
dbToken = value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public final class MemoryPersistence extends Persistence {
// tests affecting both the in-memory and SQLite-backed persistence layers. Tests can create a new
// LocalStore wrapping this Persistence instance and this will make the in-memory persistence
// layer behave as if it were actually persisting values.
private final MemoryGlobalsCache globalsCache;
private final Map<User, MemoryMutationQueue> mutationQueues;
private final Map<User, MemoryDocumentOverlayCache> overlays;
private final MemoryIndexManager indexManager;
Expand All @@ -57,6 +58,7 @@ public static MemoryPersistence createLruGcMemoryPersistence(

/** Use static helpers to instantiate */
private MemoryPersistence() {
globalsCache = new MemoryGlobalsCache();
mutationQueues = new HashMap<>();
indexManager = new MemoryIndexManager();
targetCache = new MemoryTargetCache(this);
Expand Down Expand Up @@ -117,6 +119,11 @@ MemoryRemoteDocumentCache getRemoteDocumentCache() {
return remoteDocumentCache;
}

@Override
GlobalsCache getGlobalsCache() {
return globalsCache;
}

@Override
MemoryIndexManager getIndexManager(User user) {
// We do not currently support indices for memory persistence, so we can return the same shared
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ public abstract class Persistence {
/** Creates a RemoteDocumentCache representing the persisted cache of remote documents. */
abstract RemoteDocumentCache getRemoteDocumentCache();

/** Creates GlobalCache that provides access to global values. */
abstract GlobalsCache getGlobalsCache();

/** Creates an IndexManager that manages our persisted query indexes. */
abstract IndexManager getIndexManager(User user);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
//
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.firebase.firestore.local;

import com.google.protobuf.ByteString;

public class SQLiteGlobalsCache implements GlobalsCache{

private static final String DB_TOKEN = "dbToken";
private final SQLitePersistence db;

public SQLiteGlobalsCache(SQLitePersistence persistence) {
this.db = persistence;
}

@Override
public ByteString getDbToken() {
byte[] bytes = get(DB_TOKEN);
return bytes == null ? null : ByteString.copyFrom(bytes);
}

@Override
public void setDbToken(ByteString value) {
set(DB_TOKEN, value.toByteArray());
}

private byte[] get(String global) {
return db.query("SELECT value FROM globals WHERE global = ?")
.binding(global)
.firstValue(row -> row.getBlob(0));
}

private void set(String global, byte[] value) {
db.execute(
"INSERT OR REPLACE INTO globals "
+ "(global, value) "
+ "VALUES (?, ?)",
global,
value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ RemoteDocumentCache getRemoteDocumentCache() {
return remoteDocumentCache;
}

@Override
GlobalsCache getGlobalsCache() {
return new SQLiteGlobalsCache(this);
}

@Override
void runTransaction(String action, Runnable operation) {
Logger.debug(TAG, "Starting transaction: %s", action);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class SQLiteSchema {
* The version of the schema. Increase this by one for each migration added to runMigrations
* below.
*/
static final int VERSION = 16;
static final int VERSION = 17;

/**
* The batch size for data migrations.
Expand Down Expand Up @@ -179,6 +179,10 @@ void runSchemaUpgrades(int fromVersion, int toVersion) {
createFieldIndex();
}

if (fromVersion < 17 && toVersion >= 17) {
createGlobalsTable();
}

/*
* Adding a new schema upgrade? READ THIS FIRST!
*
Expand Down Expand Up @@ -713,6 +717,18 @@ private void addPendingDataMigration(String migration) {
new String[] {migration});
}

private void createGlobalsTable() {
ifTablesDontExist(
new String[] {"globals"},
() -> {
// A table of key value pairs by user.
db.execSQL(
"CREATE TABLE globals ("
+ "global TEXT PRIMARY KEY, "
tom-andersen marked this conversation as resolved.
Show resolved Hide resolved
+ "value BLOB)");
});
}

private boolean tableExists(String table) {
return !new SQLitePersistence.Query(db, "SELECT 1=1 FROM sqlite_master WHERE tbl_name = ?")
.binding(table)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
//
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.firebase.firestore.local;

import static org.junit.Assert.assertEquals;

import com.google.protobuf.ByteString;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.nio.charset.StandardCharsets;

public abstract class GlobalsCacheTest {

private Persistence persistence;
private GlobalsCache globalsCache;

@Before
public void setUp() {
persistence = getPersistence();
globalsCache = persistence.getGlobalsCache();
}

@After
public void tearDown() {
persistence.shutdown();
}

abstract Persistence getPersistence();

@Test
public void setAndGetDbToken() {
ByteString value = ByteString.copyFrom("TestData", StandardCharsets.UTF_8);
globalsCache.setDbToken(value);
assertEquals(value, globalsCache.getDbToken());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
//
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.firebase.firestore.local;

import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class MemoryGlobalsCacheTest extends GlobalsCacheTest {

@Override
Persistence getPersistence() {
return PersistenceTestHelpers.createEagerGCMemoryPersistence();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
//
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.firebase.firestore.local;

import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class SQLiteGlobalCacheTest extends GlobalsCacheTest {

@Override
Persistence getPersistence() {
return PersistenceTestHelpers.createSQLitePersistence();
}
}
Loading