Одновременное резервирование временного интервала в базе данных Firebase Real Time (проблема перезаписи)

Я пытаюсь создать приложение, которое бронирует временной интервал, используя базу данных в реальном времени. Проблема в том, что если 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);
            }
        });

Я делаю что-то не так здесь?

0
0
869
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш код, кажется, предполагает, что запуск транзакций дает вам блокировку местоположения, но это не то, как работают транзакции в 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"
      }
    }
  }
}

Это разрешает запись, если значение еще не получено (ячейка не занята) или если пишущий пользователь — это тот, кто ранее требовал ячейку (что позволило бы им очистить ячейку).

Другие вопросы по теме