Skip to content

Commit f16564f

Browse files
committed
feat(firestore): Add support for 16MB documents
1 parent 79e26b8 commit f16564f

1 file changed

Lines changed: 160 additions & 0 deletions

File tree

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.firestore.it;
18+
19+
import static com.google.cloud.firestore.it.ITQueryTest.map;
20+
import static com.google.common.truth.Truth.assertThat;
21+
import static org.junit.Assert.assertEquals;
22+
import static org.junit.Assert.assertNotNull;
23+
import static org.junit.Assert.assertTrue;
24+
import static org.junit.Assert.fail;
25+
import static org.junit.Assume.assumeTrue;
26+
27+
import com.google.cloud.firestore.CollectionReference;
28+
import com.google.cloud.firestore.DocumentReference;
29+
import com.google.cloud.firestore.DocumentSnapshot;
30+
import com.google.cloud.firestore.FieldPath;
31+
import com.google.cloud.firestore.FieldValue;
32+
import com.google.cloud.firestore.FirestoreException;
33+
import com.google.cloud.firestore.ListenerRegistration;
34+
import com.google.cloud.firestore.QuerySnapshot;
35+
import io.grpc.Status;
36+
import java.util.Arrays;
37+
import java.util.HashMap;
38+
import java.util.Map;
39+
import java.util.concurrent.CompletableFuture;
40+
import java.util.concurrent.TimeUnit;
41+
import org.junit.Before;
42+
import org.junit.Test;
43+
import org.junit.runner.RunWith;
44+
import org.junit.runners.JUnit4;
45+
46+
@RunWith(JUnit4.class)
47+
public class ITLargeDocumentTest extends ITBaseTest {
48+
49+
static boolean runLargeDocTests() {
50+
String propertyName = "FIRESTORE_RUN_LARGE_DOC_TESTS";
51+
String runLargeTests = System.getProperty(propertyName);
52+
if (runLargeTests == null) {
53+
runLargeTests = System.getenv(propertyName);
54+
}
55+
return "YES".equalsIgnoreCase(runLargeTests) || "true".equalsIgnoreCase(runLargeTests);
56+
}
57+
58+
@Before
59+
public void checkPreconditions() {
60+
assumeTrue(runLargeDocTests());
61+
assumeTrue("NIGHTLY".equalsIgnoreCase(getTargetBackend()));
62+
assumeTrue(getFirestoreEdition() == FirestoreEdition.ENTERPRISE);
63+
}
64+
65+
@Test
66+
public void testReadLargeUnicodeDocument() throws Exception {
67+
DocumentReference docRef = firestore.collection("serverSdkTests").document("doc_15_9MB_unicode");
68+
DocumentSnapshot snapshot = docRef.get().get();
69+
assertTrue(snapshot.exists());
70+
String chunk = snapshot.getString("chunk");
71+
assertNotNull(chunk);
72+
assertEquals(8336178, chunk.length());
73+
}
74+
75+
@Test
76+
public void testQueryMultipleLargeDocuments() throws Exception {
77+
CollectionReference colRef = firestore.collection("col_large_docs");
78+
QuerySnapshot querySnapshot =
79+
colRef.whereIn(FieldPath.documentId(), Arrays.asList("doc_a", "doc_b")).get().get();
80+
assertEquals(2, querySnapshot.size());
81+
82+
DocumentSnapshot docA = querySnapshot.getDocuments().get(0);
83+
DocumentSnapshot docB = querySnapshot.getDocuments().get(1);
84+
assertNotNull(docA.getString("chunk"));
85+
assertNotNull(docB.getString("chunk"));
86+
}
87+
88+
@Test
89+
public void testWatchStreamInitialization() throws Exception {
90+
DocumentReference docRef = firestore.collection("serverSdkTests").document("doc_15_9MB_unicode");
91+
CompletableFuture<DocumentSnapshot> snapshotFuture = new CompletableFuture<>();
92+
ListenerRegistration registration =
93+
docRef.addSnapshotListener(
94+
(snapshot, error) -> {
95+
if (error != null) {
96+
snapshotFuture.completeExceptionally(error);
97+
} else if (snapshot != null && snapshot.exists()) {
98+
snapshotFuture.complete(snapshot);
99+
}
100+
});
101+
102+
try {
103+
DocumentSnapshot snapshot = snapshotFuture.get(60, TimeUnit.SECONDS);
104+
assertTrue(snapshot.exists());
105+
assertNotNull(snapshot.getString("chunk"));
106+
} finally {
107+
registration.remove();
108+
}
109+
}
110+
111+
@Test
112+
public void testTransactionReadModifyWrite() throws Exception {
113+
DocumentReference docRef = firestore.collection("serverSdkTests").document("doc_15_9MB_unicode");
114+
firestore
115+
.runTransaction(
116+
transaction -> {
117+
DocumentSnapshot snapshot = transaction.get(docRef).get();
118+
assertTrue(snapshot.exists());
119+
transaction.update(docRef, map("transaction_timestamp", FieldValue.serverTimestamp()));
120+
return null;
121+
})
122+
.get(60, TimeUnit.SECONDS);
123+
}
124+
125+
@Test
126+
public void testPaginateLargeDocuments() throws Exception {
127+
CollectionReference colRef = firestore.collection("col_large_docs");
128+
QuerySnapshot firstPage = colRef.orderBy(FieldPath.documentId()).limit(1).get().get();
129+
assertEquals(1, firstPage.size());
130+
DocumentSnapshot doc1 = firstPage.getDocuments().get(0);
131+
assertNotNull(doc1.getString("chunk"));
132+
133+
QuerySnapshot secondPage =
134+
colRef.orderBy(FieldPath.documentId()).startAfter(doc1).limit(1).get().get();
135+
assertEquals(1, secondPage.size());
136+
DocumentSnapshot doc2 = secondPage.getDocuments().get(0);
137+
assertNotNull(doc2.getString("chunk"));
138+
}
139+
140+
@Test
141+
public void testOversizedPayloadRejection() {
142+
DocumentReference docRef = firestore.collection("serverSdkTests").document("temp_oversized_doc");
143+
int targetBytes = 16 * 1024 * 1024 + 102400;
144+
StringBuilder builder = new StringBuilder(targetBytes);
145+
for (int i = 0; i < targetBytes; i++) {
146+
builder.append('a');
147+
}
148+
String largePayload = builder.toString();
149+
Map<String, Object> data = new HashMap<>();
150+
data.put("chunk", largePayload);
151+
152+
try {
153+
docRef.set(data).get(60, TimeUnit.SECONDS);
154+
fail("Setting a document exceeding the 16MB limit should fail.");
155+
} catch (Exception e) {
156+
Throwable cause = e.getCause();
157+
assertTrue(cause instanceof com.google.api.gax.rpc.InvalidArgumentException);
158+
}
159+
}
160+
}

0 commit comments

Comments
 (0)