Я пытаюсь создать приложение, которое бронирует временной интервал, используя базу данных в реальном времени. Проблема в том, что если 2 пользователя выбирают определенный временной интервал, который в данный момент свободен, и они оба нажимают кнопку «Забронировать» одновременно, данные перезаписываются.
Мой метод бронирования заключается в вставке узла с ключом = отметка времени и значение = идентификатор пользователя.
Итак, оба думают, что зарезервировали слот для себя, а на самом деле один из запросов на бронирование перезаписал другой на узле базы данных.
Я попытался использовать Сохранить данные как транзакции из документации firebase Здесь. Но по-прежнему бронирования слотов перезаписывают друг друга.
Вот мой код:
dbRef.child(String.valueOf(selectedDate.getTime())).runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
BookingSlot s = mutableData.getValue(BookingSlot.class);
if (s == null) {
//Upload new BookingSlot
dbRef.child(String.valueOf(selectedDate.getTime())).setValue(s);
);
}
return Transaction.success(mutableData);
} else {
Toast.makeText(BookingActivity.this,"Slot has just been booked!",Toast.LENGTH_LONG).show();
//The chosen time has just been booked,
}
return Transaction.abort();
}
@Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
Log.e("Booking", "postTransaction:onComplete:" + databaseError);
}
});
Я делаю что-то не так здесь?
Ваш код, кажется, предполагает, что запуск транзакций дает вам блокировку местоположения, но это не то, как работают транзакции в Firebase. Вместо этого транзакции в Firebase работают с логикой сравнения и установки: клиент сообщает вам, каково (по его мнению) текущее значение, а вы сообщаете ему, каким в этом случае становится новое значение, возвращая это новое значение.
Поэтому вместо того, чтобы вызывать .setValue(s) на месте, вы должны вернуть s в MutableData:
dbRef.child(String.valueOf(selectedDate.getTime())).runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
BookingSlot s = mutableData.getValue(BookingSlot.class);
if (s == null) {
mutableData.setValue(uid); // TODO: pass in the UID of the user who's claiming this slot
return Transaction.success(mutableData);
} else {
Toast.makeText(BookingActivity.this,"Slot has just been booked!",Toast.LENGTH_LONG).show();
return Transaction.abort();
}
}
@Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
Log.e("Booking", "postTransaction:onComplete:" + databaseError);
}
});
С приведенным выше кодом клиенты не будут перезаписывать значения друг друга.
Но с Firebase вы всегда должны учитывать, что злоумышленник может написать свой собственный код для вашей базы данных, поскольку он может найти данные конфигурации в APK вашего приложения. Таким образом, вы также должны обеспечить в правилах безопасности Firebase на стороне сервера, что каждый слот может быть востребован только один раз.
Если заказы хранятся под /bookings, это можно сделать примерно так:
{
"rules": {
"bookings": {
"$timeslot": {
".write": "data.val() === null || data.val() === auth.uid"
}
}
}
}
Это разрешает запись, если значение еще не получено (ячейка не занята) или если пишущий пользователь — это тот, кто ранее требовал ячейку (что позволило бы им очистить ячейку).