I'm facing a really strange decision here about the performance of the following scenarios with dealing with Firebase database, what I'm doing is I generate a random customerId
for alternative use and store it for that inside profile (I still use Firebase uid
but it is just for "friendly numeric number" as a client wants).
What I'm trying to do one of the following:
When I get the request:
UserVO createdUser = Json.fromJson(getRequestBodyAsJson(), UserVO.class);
CompletableFuture<String> checkCustomerIdCompletableFuture = firebaseDatabaseService.buildUniqueCustomerId();
return checkCustomerIdCompletableFuture.thenApply(customerId -> {
createdUser.setCustomerId(customerId);
return firebaseDatabaseService.addToUserProfile(createdUser.getId(), getObjectAsMapOfObjects(createdUser));
}).thenCompose(completableFuture -> CompletableFuture.completedFuture(ok(Json.toJson(createdUser))));
The customerId is always indexed inside profiles:
"profiles":{
"$uid":{
".read":"$uid === auth.uid",
".write":"$uid === auth.uid",
},
".indexOn": ["customerId", "email"]
}
And on both cases, the profile of user should be like this:
"profiles" : {
"jiac4QpEfggRTuKuTfVOisRGFJn1" : {
"contactPhone" : "",
"createdAt" : 1499606268255,
"customerId" : 4998721187, // OR "A-4998721187" as string
"email" : "almothafar@example.com",
"firstName" : "Al-Mothafar",
"fullName" : "Al-Mothafar Al-Hasan",
"id" : "jiac4QpEfggRTuKuTfVOisRGFJn1",
"lastName" : "Al-Hasan2",
"updatedAt" : 1499857345960,
"verified" : false
}
}
I have 2 options here for buildUniqueCustomerId()
:
The first one is directly querying inside profiles
about customerId
, and return the unique id, using queryByChild
and the customerId
is indexed:
public CompletableFuture<String> buildUniqueCustomerId() {
String customerId = String.valueOf(System.currentTimeMillis()).substring(1, 9).concat(RandomStringUtils.randomNumeric(2));
CompletableFuture<String> dataSnapshotCompletableFuture = new CompletableFuture<>();
firebaseDatabaseProvider.getUserDataReference().child("/profiles").orderByChild("customerId").equalTo(customerId).limitToFirst(1)
.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
if (snapshot.exists()) {
buildUniqueCustomerId();
} else {
dataSnapshotCompletableFuture.complete(customerId);
}
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
if (snapshot.exists()) {
buildUniqueCustomerId();
} else {
dataSnapshotCompletableFuture.complete(customerId);
}
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
dataSnapshotCompletableFuture.completeExceptionally(new BusinessException("Child Remove"));
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
dataSnapshotCompletableFuture.completeExceptionally(new BusinessException("Child MOved"));
}
@Override
public void onCancelled(DatabaseError error) {
dataSnapshotCompletableFuture.completeExceptionally(new BusinessException(error.getMessage()));
}
});
return dataSnapshotCompletableFuture;
}
Another way is to, create new node like reservedCustomerIds
, check if customerId is reserved already, and push that id to that array in case it is not reserved and return the ID for use, in this case, customerId
is a key:
public CompletableFuture<String> buildUniqueCustomerId() {
String customerId = "A-".concat(String.valueOf(System.currentTimeMillis()).substring(1, 9).concat(RandomStringUtils.randomNumeric(2)));
String customerRef = String.format("/reservedCustomerIds/%s", customerId);
return firebaseDatabaseProvider.fetchObjectAtRef("/usersData".concat(customerRef))
.thenCompose(dataSnapshot -> {
if (dataSnapshot.getValue() != null) {
return buildUniqueCustomerId();
} else {
return CompletableFuture.completedFuture(customerId);
}
})
.thenCompose((newCustomerId) -> this.updateObjectData(true, customerRef).thenApply(aVoid -> newCustomerId))
.exceptionally(throwable -> {
Logger.error(throwable.getMessage());
return null;
});
}
The first way code needs some cleaning, but it is just quick kick in, but you can see that the second way is shorter in the code but it is one more step to store that ID, also, it will have additional storage reservedCustomerIds
just for check IDs:
"reservedCustomerIds" : {
"A-4998721187" : true,
"A-4998722342" : true,
"A-4998722222" : true,
"A-4998724444" : true,
"A-4998725555" : true,
}
Which one the best for performance, faster to check customerId uniqueness? use customerId as a key in with extra storage, or use customerId itself inside profiles with .indexOn
?
P.S: in comments or full answer if you can give me a link(s) for how firebase indexing, or querying, I'll be so thankful.