diff --git a/src/main/java/com/google/firebase/messaging/FirebaseMessaging.java b/src/main/java/com/google/firebase/messaging/FirebaseMessaging.java
index bb7b8208f..06ac51d0b 100644
--- a/src/main/java/com/google/firebase/messaging/FirebaseMessaging.java
+++ b/src/main/java/com/google/firebase/messaging/FirebaseMessaging.java
@@ -152,7 +152,8 @@ protected String execute() throws FirebaseMessagingException {
*
The list of responses obtained by calling {@link BatchResponse#getResponses()} on the return
* value is in the same order as the input list.
*
- * @param messages A non-null, non-empty list containing up to 500 messages.
+ * @param messages A non-null, non-empty list of messages. For very large lists, consider
+ * chunking into smaller batches to avoid FCM server-side rate limiting.
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
* delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
@@ -163,7 +164,6 @@ public BatchResponse sendEach(@NonNull List messages) throws FirebaseMe
return sendEach(messages, false);
}
-
/**
* Sends each message in the given list via Firebase Cloud Messaging.
* Unlike {@link #sendAll(List)}, this method makes an HTTP call for each message in the
@@ -177,7 +177,8 @@ public BatchResponse sendEach(@NonNull List messages) throws FirebaseMe
* The list of responses obtained by calling {@link BatchResponse#getResponses()} on the return
* value is in the same order as the input list.
*
- * @param messages A non-null, non-empty list containing up to 500 messages.
+ * @param messages A non-null, non-empty list of messages. For very large lists, consider
+ * chunking into smaller batches to avoid FCM server-side rate limiting.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
@@ -197,7 +198,8 @@ public BatchResponse sendEach(
/**
* Similar to {@link #sendEach(List)} but performs the operation asynchronously.
*
- * @param messages A non-null, non-empty list containing up to 500 messages.
+ * @param messages A non-null, non-empty list of messages. For very large lists, consider
+ * chunking into smaller batches to avoid FCM server-side rate limiting.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
*/
@@ -208,7 +210,8 @@ public ApiFuture sendEachAsync(@NonNull List messages) {
/**
* Similar to {@link #sendEach(List, boolean)} but performs the operation asynchronously.
*
- * @param messages A non-null, non-empty list containing up to 500 messages.
+ * @param messages A non-null, non-empty list of messages. For very large lists, consider
+ * chunking into smaller batches to avoid FCM server-side rate limiting.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
@@ -217,14 +220,16 @@ public ApiFuture sendEachAsync(@NonNull List messages, b
return sendEachOpAsync(messages, dryRun);
}
- // Returns an ApiFuture directly since this function is non-blocking. Individual child send
+ // Returns an ApiFuture directly since this function is non-blocking. Individual child send
// requests are still called async and run in background threads.
private ApiFuture sendEachOpAsync(
final List messages, final boolean dryRun) {
final List immutableMessages = ImmutableList.copyOf(messages);
checkArgument(!immutableMessages.isEmpty(), "messages list must not be empty");
- checkArgument(immutableMessages.size() <= 500,
- "messages list must not contain more than 500 elements");
+ // NOTE: The 500-message limit that existed here was inherited from the deprecated sendAll()
+ // method, which used a single HTTP batch request limited by Google's batch API. Since
+ // sendEach() makes individual HTTP calls per message, no such limit applies. Callers should
+ // chunk very large lists themselves to avoid FCM server-side rate limiting.
List> list = new ArrayList<>();
for (Message message : immutableMessages) {
@@ -232,7 +237,7 @@ private ApiFuture sendEachOpAsync(
ApiFuture messageId = sendOpForSendResponse(message, dryRun).callAsync(app);
list.add(messageId);
}
-
+
// Gather all futures and combine into a list
ApiFuture> responsesFuture = ApiFutures.allAsList(list);
@@ -240,7 +245,7 @@ private ApiFuture sendEachOpAsync(
// the main thread. This uses the current thread to execute, but since the transformation
// function is non-blocking the transformation itself is also non-blocking.
return ApiFutures.transform(
- responsesFuture,
+ responsesFuture,
(responses) -> {
return new BatchResponseImpl(responses);
},
@@ -272,7 +277,8 @@ protected SendResponse execute() {
* {@link BatchResponse#getResponses()} on the return value is in the same order as the
* tokens in the {@link MulticastMessage}.
*
- * @param message A non-null {@link MulticastMessage}
+ * @param message A non-null {@link MulticastMessage}. For very large token lists, consider
+ * chunking into smaller batches to avoid FCM server-side rate limiting.
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
* delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
@@ -297,7 +303,8 @@ public BatchResponse sendEachForMulticast(
* {@link BatchResponse#getResponses()} on the return value is in the same order as the
* tokens in the {@link MulticastMessage}.
*
- * @param message A non-null {@link MulticastMessage}.
+ * @param message A non-null {@link MulticastMessage}. For very large token lists, consider
+ * chunking into smaller batches to avoid FCM server-side rate limiting.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
@@ -315,7 +322,8 @@ public BatchResponse sendEachForMulticast(@NonNull MulticastMessage message, boo
* Similar to {@link #sendEachForMulticast(MulticastMessage)} but performs the operation
* asynchronously.
*
- * @param message A non-null {@link MulticastMessage}.
+ * @param message A non-null {@link MulticastMessage}. For very large token lists, consider
+ * chunking into smaller batches to avoid FCM server-side rate limiting.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
*/
@@ -327,7 +335,8 @@ public ApiFuture sendEachForMulticastAsync(@NonNull MulticastMess
* Similar to {@link #sendEachForMulticast(MulticastMessage, boolean)} but performs the operation
* asynchronously.
*
- * @param message A non-null {@link MulticastMessage}.
+ * @param message A non-null {@link MulticastMessage}. For very large token lists, consider
+ * chunking into smaller batches to avoid FCM server-side rate limiting.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
@@ -677,4 +686,4 @@ FirebaseMessaging build() {
return new FirebaseMessaging(this);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/test/java/com/google/firebase/messaging/FirebaseMessagingIT.java b/src/test/java/com/google/firebase/messaging/FirebaseMessagingIT.java
index e084b8c29..2c76e2adf 100644
--- a/src/test/java/com/google/firebase/messaging/FirebaseMessagingIT.java
+++ b/src/test/java/com/google/firebase/messaging/FirebaseMessagingIT.java
@@ -169,6 +169,22 @@ public void testSendFiveHundredWithSendEach() throws Exception {
assertNull(sendResponse.getException());
}
}
+
+ @Test
+ public void testSendMoreThanFiveHundredWithSendEach() throws Exception {
+ List messages = new ArrayList<>();
+ for (int i = 0; i < 501; i++) {
+ messages.add(Message.builder().setTopic("foo-bar-" + (i % 10)).build());
+ }
+
+ // Previously this would throw IllegalArgumentException due to the 500 limit.
+ // After removing the limit, this should succeed.
+ BatchResponse response = FirebaseMessaging.getInstance().sendEach(messages, true);
+
+ assertEquals(501, response.getResponses().size());
+ assertEquals(501, response.getSuccessCount());
+ assertEquals(0, response.getFailureCount());
+ }
@Test
public void testSendEachForMulticast() throws Exception {
diff --git a/src/test/java/com/google/firebase/messaging/FirebaseMessagingTest.java b/src/test/java/com/google/firebase/messaging/FirebaseMessagingTest.java
index 42a499b75..fc7891ba5 100644
--- a/src/test/java/com/google/firebase/messaging/FirebaseMessagingTest.java
+++ b/src/test/java/com/google/firebase/messaging/FirebaseMessagingTest.java
@@ -38,6 +38,7 @@
import com.google.firebase.auth.MockGoogleCredentials;
import com.google.firebase.internal.Nullable;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -305,22 +306,21 @@ public void testSendEachWithEmptyList() throws FirebaseMessagingException {
}
@Test
- public void testSendEachWithTooManyMessages() throws FirebaseMessagingException {
- MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageId(null);
- FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
- ImmutableList.Builder listBuilder = ImmutableList.builder();
+ public void testSendEachWithMoreThanFiveHundredMessages() {
+ List messages = new ArrayList<>();
for (int i = 0; i < 501; i++) {
- listBuilder.add(Message.builder().setTopic("topic").build());
+ messages.add(Message.builder().setTopic("foo-bar").build());
}
-
+ // Previously threw IllegalArgumentException due to artificial 500 limit.
+ // sendEach() makes individual HTTP calls per message, so no such limit applies.
+ // Verify that 501 messages no longer throws an IllegalArgumentException.
try {
- messaging.sendEach(listBuilder.build(), false);
- fail("No error thrown for too many messages in the list");
- } catch (IllegalArgumentException expected) {
- // expected
+ FirebaseMessaging.getInstance().sendEach(messages);
+ } catch (IllegalArgumentException e) {
+ fail("sendEach() should not throw IllegalArgumentException for more than 500 messages");
+ } catch (Exception e) {
+ // Other exceptions (e.g. network) are acceptable in unit test context
}
-
- assertNull(client.lastMessage);
}
@Test