Skip to content

Commit cb986fe

Browse files
DeferredValue changes for ServerValue.increment()
Ports firebase/firebase-ios-sdk@aa67e19
1 parent 7d0a217 commit cb986fe

File tree

9 files changed

+202
-46
lines changed

9 files changed

+202
-46
lines changed

firebase-database/src/androidTest/java/com/google/firebase/database/DataTest.java

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2747,7 +2747,18 @@ public void onComplete(DatabaseError error, boolean committed, DataSnapshot curr
27472747
}
27482748

27492749
@Test
2750-
public void testServerIncrementOverwritesExistingData()
2750+
public void testServerIncrementOverwritesExistingDataOnline()
2751+
throws DatabaseException, TimeoutException, InterruptedException {
2752+
serverIncrementOverwritesExistingData(true);
2753+
}
2754+
2755+
@Test
2756+
public void testServerIncrementOverwritesExistingDataOffline()
2757+
throws DatabaseException, TimeoutException, InterruptedException {
2758+
serverIncrementOverwritesExistingData(false);
2759+
}
2760+
2761+
public void serverIncrementOverwritesExistingData(boolean online)
27512762
throws DatabaseException, TimeoutException, InterruptedException {
27522763
DatabaseConfig cfg = IntegrationTestHelpers.newTestConfig();
27532764
DatabaseReference ref = IntegrationTestHelpers.rootWithConfig(cfg);
@@ -2756,7 +2767,9 @@ public void testServerIncrementOverwritesExistingData()
27562767
List<Object> expectedValues = new ArrayList<>();
27572768

27582769
// Going offline ensures that local events get queued up before server events
2759-
IntegrationTestHelpers.goOffline(cfg);
2770+
if (!online) {
2771+
IntegrationTestHelpers.goOffline(cfg);
2772+
}
27602773

27612774
// Phaser is the closest built-in to a bidrectional latch. We could use a semaphore with a fixed
27622775
// number of permits, but the test would be fragile since the permit count isn't closely related
@@ -2808,12 +2821,25 @@ public void onCancelled(DatabaseError error) {}
28082821
assertEquals(expectedValues, foundValues);
28092822
} finally {
28102823
ref.removeEventListener(listener);
2811-
IntegrationTestHelpers.goOnline(cfg);
2824+
if (!online) {
2825+
IntegrationTestHelpers.goOnline(cfg);
2826+
}
28122827
}
28132828
}
28142829

28152830
@Test
2816-
public void testServerIncrementPriority()
2831+
public void testServerIncrementPriorityOnline()
2832+
throws DatabaseException, TimeoutException, InterruptedException {
2833+
serverIncrementPriority(true);
2834+
}
2835+
2836+
@Test
2837+
public void testServerIncrementPriorityOffline()
2838+
throws DatabaseException, TimeoutException, InterruptedException {
2839+
serverIncrementPriority(false);
2840+
}
2841+
2842+
public void serverIncrementPriority(boolean online)
28172843
throws DatabaseException, TimeoutException, InterruptedException {
28182844
DatabaseConfig cfg = IntegrationTestHelpers.newTestConfig();
28192845
DatabaseReference ref = IntegrationTestHelpers.rootWithConfig(cfg);
@@ -2822,7 +2848,9 @@ public void testServerIncrementPriority()
28222848
List<Object> expectedPriorities = new ArrayList<>();
28232849

28242850
// Going offline ensures that local events get queued up before server events
2825-
IntegrationTestHelpers.goOffline(cfg);
2851+
if (!online) {
2852+
IntegrationTestHelpers.goOffline(cfg);
2853+
}
28262854

28272855
// Phaser is the closest built-in to a bidrectional latch. We could use a semaphore with a fixed
28282856
// number of permits, but the test would be fragile since the permit count isn't closely related
@@ -2859,7 +2887,9 @@ public void onCancelled(DatabaseError error) {}
28592887
assertEquals(expectedPriorities, foundPriorities);
28602888
} finally {
28612889
ref.removeEventListener(listener);
2862-
IntegrationTestHelpers.goOnline(cfg);
2890+
if (!online) {
2891+
IntegrationTestHelpers.goOnline(cfg);
2892+
}
28632893
}
28642894
}
28652895

firebase-database/src/main/java/com/google/firebase/database/ServerValue.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public class ServerValue {
4545
* @param delta the amount to modify the current value atomically.
4646
* @return a placeholder value for modifying data atomically server-side.
4747
*/
48+
@NonNull
4849
static final Object increment(long delta) {
4950
return createParameterizedServerValuePlaceholder(ServerValues.NAME_OP_INCREMENT, delta);
5051
}
@@ -61,6 +62,7 @@ static final Object increment(long delta) {
6162
* @param delta the amount to modify the current value atomically.
6263
* @return a placeholder value for modifying data atomically server-side.
6364
*/
65+
@NonNull
6466
static final Object increment(double delta) {
6567
return createParameterizedServerValuePlaceholder(ServerValues.NAME_OP_INCREMENT, delta);
6668
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.database.core;
16+
17+
import com.google.firebase.database.snapshot.ChildKey;
18+
import com.google.firebase.database.snapshot.Node;
19+
import java.util.ArrayList;
20+
21+
/** A DeferredValueProvider computes the value of a Node only when {@link #node()} is invoked. */
22+
public class DeferredValueProvider implements ValueProvider {
23+
24+
final private SyncTree syncTree;
25+
final private Path path;
26+
27+
DeferredValueProvider(SyncTree syncTree, Path path) {
28+
this.syncTree = syncTree;
29+
this.path = path;
30+
}
31+
32+
@Override
33+
public ValueProvider getImmediateChild(ChildKey childKey) {
34+
Path child = path.child(childKey);
35+
return new DeferredValueProvider(syncTree, child);
36+
}
37+
38+
@Override
39+
public Node node() {
40+
return syncTree.calcCompleteEventCache(path, new ArrayList<>());
41+
}
42+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.database.core;
16+
17+
import com.google.firebase.database.snapshot.ChildKey;
18+
import com.google.firebase.database.snapshot.Node;
19+
20+
/**
21+
* An ExistingValueProvider implements the ValueProvider interface for a Node whose value is known.
22+
*/
23+
public class ExistingValueProvider implements ValueProvider {
24+
final private Node node;
25+
26+
ExistingValueProvider(Node node) {
27+
this.node = node;
28+
}
29+
30+
@Override
31+
public ValueProvider getImmediateChild(ChildKey childKey) {
32+
Node child = node.getImmediateChild(childKey);
33+
return new ExistingValueProvider(child);
34+
}
35+
36+
@Override
37+
public Node node() {
38+
return node;
39+
}
40+
}

firebase-database/src/main/java/com/google/firebase/database/core/Repo.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,14 @@ public void onRequestResult(String optErrorCode, String optErrorMessage) {
227227
}
228228
lastWriteId = write.getWriteId();
229229
nextWriteId = write.getWriteId() + 1;
230-
Node existing = serverSyncTree.calcCompleteEventCache(write.getPath(), new ArrayList<>());
231230
if (write.isOverwrite()) {
232231
if (operationLogger.logsDebug()) {
233232
operationLogger.debug("Restoring overwrite with id " + write.getWriteId());
234233
}
235234
connection.put(write.getPath().asList(), write.getOverwrite().getValue(true), onComplete);
236235
Node resolved =
237-
ServerValues.resolveDeferredValueSnapshot(write.getOverwrite(), existing, serverValues);
236+
ServerValues.resolveDeferredValueSnapshot(
237+
write.getOverwrite(), serverSyncTree, write.getPath(), serverValues);
238238
serverSyncTree.applyUserOverwrite(
239239
write.getPath(),
240240
write.getOverwrite(),
@@ -248,7 +248,8 @@ public void onRequestResult(String optErrorCode, String optErrorMessage) {
248248
}
249249
connection.merge(write.getPath().asList(), write.getMerge().getValue(true), onComplete);
250250
CompoundWrite resolved =
251-
ServerValues.resolveDeferredValueMerge(write.getMerge(), existing, serverValues);
251+
ServerValues.resolveDeferredValueMerge(
252+
write.getMerge(), serverSyncTree, write.getPath(), serverValues);
252253
serverSyncTree.applyUserMerge(
253254
write.getPath(), write.getMerge(), resolved, write.getWriteId(), /*persist=*/ false);
254255
}
@@ -483,9 +484,8 @@ public void updateChildren(
483484

484485
// Start with our existing data and merge each child into it.
485486
Map<String, Object> serverValues = ServerValues.generateServerValues(serverClock);
486-
Node existing = serverSyncTree.calcCompleteEventCache(path, new ArrayList<>());
487487
CompoundWrite resolved =
488-
ServerValues.resolveDeferredValueMerge(updates, existing, serverValues);
488+
ServerValues.resolveDeferredValueMerge(updates, serverSyncTree, path, serverValues);
489489

490490
final long writeId = this.getNextWriteId();
491491
List<? extends Event> events =

firebase-database/src/main/java/com/google/firebase/database/core/ServerValues.java

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package com.google.firebase.database.core;
1616

1717
import com.google.firebase.database.core.utilities.Clock;
18+
import com.google.firebase.database.core.utilities.Utilities;
1819
import com.google.firebase.database.snapshot.ChildKey;
1920
import com.google.firebase.database.snapshot.ChildrenNode;
2021
import com.google.firebase.database.snapshot.Node;
@@ -34,8 +35,8 @@ public static Map<String, Object> generateServerValues(Clock clock) {
3435
return values;
3536
}
3637

37-
public static Object resolveDeferredValue(
38-
Object value, Node existing, Map<String, Object> serverValues) {
38+
public static Object resolveDeferredLeafValue(
39+
Object value, ValueProvider existing, Map<String, Object> serverValues) {
3940
if (!(value instanceof Map)) {
4041
return value;
4142
}
@@ -47,7 +48,7 @@ public static Object resolveDeferredValue(
4748
Object op = mapValue.get(NAME_SUBKEY_SERVERVALUE);
4849
Object res = null;
4950
if (op instanceof String) {
50-
res = resolveScalarDeferredValue((String) op, existing, serverValues);
51+
res = resolveScalarDeferredValue((String) op, serverValues);
5152
} else if (op instanceof Map) {
5253
res = resolveComplexDeferredValue((Map) op, existing, serverValues);
5354
}
@@ -57,16 +58,15 @@ public static Object resolveDeferredValue(
5758
return res;
5859
}
5960

60-
static Object resolveScalarDeferredValue(
61-
String op, Node existing, Map<String, Object> serverValues) {
61+
static Object resolveScalarDeferredValue(String op, Map<String, Object> serverValues) {
6262
if (NAME_OP_TIMESTAMP.equals(op) && serverValues.containsKey(op)) {
6363
return serverValues.get(op);
6464
}
6565
return null;
6666
}
6767

6868
static Object resolveComplexDeferredValue(
69-
Map<String, Object> op, Node existing, Map<String, Object> serverValues) {
69+
Map<String, Object> op, ValueProvider existing, Map<String, Object> serverValues) {
7070
// Only supported complex op so far
7171
if (!op.containsKey(NAME_OP_INCREMENT)) {
7272
return null;
@@ -80,11 +80,12 @@ static Object resolveComplexDeferredValue(
8080
Number increment = (Number) incrObject;
8181

8282
// Incrementing a non-number sets the value to the incremented amount
83-
if (!(existing.isLeafNode() && existing.getValue() instanceof Number)) {
83+
Node existingNode = existing.node();
84+
if (!(existingNode.isLeafNode() && existingNode.getValue() instanceof Number)) {
8485
return increment;
8586
}
8687

87-
Number existingVal = (Number) existing.getValue();
88+
Number existingVal = (Number) existingNode.getValue();
8889
if (canBeRepresentedAsLong(increment) && canBeRepresentedAsLong(existingVal)) {
8990
long x = increment.longValue();
9091
long y = existingVal.longValue();
@@ -99,32 +100,29 @@ static Object resolveComplexDeferredValue(
99100
return increment.doubleValue() + existingVal.doubleValue();
100101
}
101102

102-
public static SparseSnapshotTree resolveDeferredValueTree(
103-
SparseSnapshotTree tree, Node existing, final Map<String, Object> serverValues) {
104-
final SparseSnapshotTree resolvedTree = new SparseSnapshotTree();
105-
tree.forEachTree(
106-
new Path(""),
107-
new SparseSnapshotTree.SparseSnapshotTreeVisitor() {
108-
@Override
109-
public void visitTree(Path prefixPath, Node tree) {
110-
resolvedTree.remember(
111-
prefixPath,
112-
resolveDeferredValueSnapshot(tree, existing.getChild(prefixPath), serverValues));
113-
}
114-
});
115-
return resolvedTree;
103+
public static Node resolveDeferredValueSnapshot(
104+
Node data, Node existing, final Map<String, Object> serverValues) {
105+
return resolveDeferredValueSnapshot(data, new ExistingValueProvider(existing), serverValues);
116106
}
117107

118108
public static Node resolveDeferredValueSnapshot(
119-
Node data, Node existing, final Map<String, Object> serverValues) {
120-
Object priorityVal =
121-
resolveDeferredValue(data.getPriority().getValue(), existing.getPriority(), serverValues);
122-
Node priority = PriorityUtilities.parsePriority(priorityVal);
109+
Node data, SyncTree syncTree, Path path, final Map<String, Object> serverValues) {
110+
return resolveDeferredValueSnapshot(
111+
data, new DeferredValueProvider(syncTree, path), serverValues);
112+
}
123113

114+
private static Node resolveDeferredValueSnapshot(
115+
Node data, ValueProvider existing, final Map<String, Object> serverValues) {
116+
Object rawPriority = data.getPriority().getValue();
117+
Object priority =
118+
resolveDeferredLeafValue(
119+
rawPriority,
120+
existing.getImmediateChild(ChildKey.fromString(".priority")),
121+
serverValues);
124122
if (data.isLeafNode()) {
125-
Object value = resolveDeferredValue(data.getValue(), existing, serverValues);
126-
if (!value.equals(data.getValue()) || !priority.equals(data.getPriority())) {
127-
return NodeUtilities.NodeFromJSON(value, priority);
123+
Object value = resolveDeferredLeafValue(data.getValue(), existing, serverValues);
124+
if (!value.equals(data.getValue()) || !Utilities.equals(priority, rawPriority)) {
125+
return NodeUtilities.NodeFromJSON(value, PriorityUtilities.parsePriority(priority));
128126
}
129127
return data;
130128
} else if (data.isEmpty()) {
@@ -145,22 +143,22 @@ public void visitChild(ChildKey name, Node child) {
145143
}
146144
});
147145
if (!holder.getRootNode().getPriority().equals(priority)) {
148-
return holder.getRootNode().updatePriority(priority);
146+
return holder.getRootNode().updatePriority(PriorityUtilities.parsePriority(priority));
149147
} else {
150148
return holder.getRootNode();
151149
}
152150
}
153151
}
154152

155153
public static CompoundWrite resolveDeferredValueMerge(
156-
CompoundWrite merge, Node existing, final Map<String, Object> serverValues) {
154+
CompoundWrite merge, SyncTree syncTree, Path path, final Map<String, Object> serverValues) {
157155
CompoundWrite write = CompoundWrite.emptyWrite();
158156
for (Map.Entry<Path, Node> entry : merge) {
157+
ValueProvider deferredValue = new DeferredValueProvider(syncTree, path.child(entry.getKey()));
159158
write =
160159
write.addWrite(
161160
entry.getKey(),
162-
resolveDeferredValueSnapshot(
163-
entry.getValue(), existing.getChild(entry.getKey()), serverValues));
161+
resolveDeferredValueSnapshot(entry.getValue(), deferredValue, serverValues));
164162
}
165163
return write;
166164
}

firebase-database/src/main/java/com/google/firebase/database/core/SyncTree.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,12 +249,12 @@ public List<? extends Event> call() {
249249
if (write.isOverwrite()) {
250250
Node resolvedNode =
251251
ServerValues.resolveDeferredValueSnapshot(
252-
write.getOverwrite(), existing, serverValues);
252+
write.getOverwrite(), SyncTree.this, write.getPath(), serverValues);
253253
persistenceManager.applyUserWriteToServerCache(write.getPath(), resolvedNode);
254254
} else {
255255
CompoundWrite resolvedMerge =
256256
ServerValues.resolveDeferredValueMerge(
257-
write.getMerge(), existing, serverValues);
257+
write.getMerge(), SyncTree.this, write.getPath(), serverValues);
258258
persistenceManager.applyUserWriteToServerCache(write.getPath(), resolvedMerge);
259259
}
260260
}

0 commit comments

Comments
 (0)