# #firebase #google-cloud-firestore
Вопрос:
При извлечении данных из Firestore есть возможность принудительного извлечения с сервера. Параметр по умолчанию-кэш и сервер, как определено Firestore.
У меня есть определенное использование, когда узел управления и управления выдает команды в режиме реального времени удаленным узлам, поддерживаемым Firestore. Это требует, чтобы обновления выполнялись на сервере (или при сбое), чтобы узел Camp;C был уверен в выполнении (или сбое) в режиме реального времени. Что я хотел бы сделать, так это отключить использование кэша с этими обновлениями. Я не нашел способа сделать это. Возможно ли это при нынешних возможностях Firestore?
Обратите внимание, что нежелательно отключать кэширование Firestore на глобальном уровне, так как кэш полезен в других ситуациях.
—-РЕДАКТИРОВАТЬ——
На основе ответов я создал этот метод обновления, который пытается принудительно обновить сервер с помощью транзакции.
Пара заметок:
- Это код дротика.
- Utils.xyz-это внутренняя библиотека, и в данном случае она используется для ведения журнала.
- Я снизил скорость сети для теста, чтобы имитировать плохое сетевое соединение.
- Тайм — аут установлен на 5 секунд.
Вот вывод из моего журнала:
я/предсердий (22601): [2021-06-06 22:35:30] [осторожностью.Отладка] [FirestoreModel] [обновление] [мы находимся здесь!]
Я/предсердий (22601): [2021-06-06 22:35:47] [осторожностью.Отладка] [FirestoreModel] [обновление] [мы находимся здесь!]
Я/предсердий (22601): [2021-06-06 22:36:02] [осторожностью.Отладка] [FirestoreModel] [обновление] [мы находимся здесь!]
Я/предсердий (22601): [2021-06-06 22:37:18] [осторожностью.Отладка] [FirestoreModel] [обновление] [мы находимся здесь!]
Я/предсердий (22601): [2021-06-06 22:37:20] [осторожностью.ИНФОРМАЦИЯ] [Модель FirestoreModel] [обновление] [Транзакция завершена в 110929 мс.]
Firebase полностью игнорирует тайм-аут в 5 секунд; пытается обновить 4 раза каждый раз с интервалом ~15 секунд и, наконец, успешно работает через 110 секунд. Я получаю ответ в режиме реального времени в течение нескольких секунд (5 секунд) или сбой.
Future<void> update(
Map<String, dynamic> data, {
WriteBatch batch,
Transaction transaction,
bool forceServer = false,
}) async {
// If updating there must be an id.
assert(this.id != null);
// Only one of batch or transaction can be non-null.
assert(batch == null || transaction == null);
// When forcing to update on server no transaction or batch is allowed.
assert(!forceServer || (batch == null amp;amp; transaction == null));
try {
if (forceServer) {
DateTime start = DateTime.now();
await FirebaseFirestore.instance.runTransaction(
(transaction) async {
await update(data, transaction: transaction);
Utils.logDebug('We are here!');
},
timeout: Duration(seconds: 5),
);
Utils.logDebug('Transaction successful in ${DateTime.now().difference(start).inMilliseconds}ms.');
} else {
DocumentReference ref =
FirebaseFirestore.instance.collection(collection).doc(this.id);
if (batch != null)
batch.update(ref, data);
else if (transaction != null)
transaction.update(ref, data);
else
await ref.update(data);
}
} catch (e, s) {
Utils.logException('Error updating document $id in $collection.', e, s);
// Propagate the error.
rethrow;
}
}
Комментарии:
1. Я не уверен, что полностью понимаю. Сначала вы говорите о слушателях, но затем, похоже, переключаетесь на написание обновлений. Можете ли вы более четко объяснить, какой это, или (еще лучше) показать код для операции, о которой вы спрашиваете?
2. Первый абзац-это утверждение, а второй-проблема. Проблема в том, что я не могу заставить обновление() находиться на сервере, в то время как опция принудительного чтения с сервера существует с помощью get(). Один из ответов предложил использовать транзакцию для достижения этой цели. Попробую это сделать и прокомментирую.
Ответ №1:
Для этого требуется, чтобы обновления выполнялись на сервере (или не выполнялись).
Для этого вы могли бы использовать Transactions and batched writes.
Транзакции будут завершены неудачно, когда клиент находится в автономном режиме. Проверьте док
Комментарии:
1. Спасибо, это звучит так, как будто что-то может сработать. Я проверю это и доложу об этом.
2. Конечно, дай мне знать
3. Я попробовал ваше предложение. Пожалуйста, ознакомьтесь с отредактированным вопросом. При пакетной записи все еще может использоваться кэш. Согласно документации, сделка не проводится. Однако транзакция, похоже, не соблюдает тайм-аут и в одном тесте обновляет Firestore через 110 секунд. Неясно, полностью ли он избегает кэша. Это, конечно, не в режиме реального времени (быстро в течение нескольких секунд), который необходим для моего варианта использования.
Ответ №2:
Чтобы получить оперативные данные с сервера один раз, вы должны использовать:
firebase.firestore()
.doc("somecollection/docId")
.get({ source: "server" })
.then((snapshot) => {
// if here, snapshot.data() is from the server
// TODO: do something with data
})
.catch((err) => {
// if here, get() encountered an error (insufficient permissions, server not available, etc)
// TODO: handle the error
});
Чтобы получать данные в реальном времени только с сервера (игнорируя кэш), вы бы использовали:
const unsubscribe = firebase.firestore()
.doc("somecollection/docId")
.onSnapshot({ includeMetadataChanges: true }, {
next(snapshot) {
// ignore cache data
if (snapshot.metadata.fromCache) return;
// if here, snapshot.data() is from the server
// TODO: do something with data
},
error(err) {
// if here, onSnapshot() encountered an error (insufficient permissions, etc)
// TODO: handle the error
}
});
Для записи на сервер вы должны использовать обычные операции записи — delete()
, set()
, и update()
;, поскольку все они возвращают обещания, которые не будут разрешены, пока клиент находится в автономном режиме. Если они разрешены, данные, хранящиеся на сервере, были обновлены.
Чтобы проверить, находитесь ли вы в Сети или нет, вы можете попробовать удалить несуществующий документ с сервера следующим образом:
/**
* Attempts to fetch the non-existant document `/.info/connected` to determine
* if a connection to the server is available.
* @return {Promise<boolean>} promise that resolves to a boolean indicating
* whether a server connection is available
*/
function isCurrentlyOnline() {
// unlike RTDB, this data doesn't exist and has no function
// must be made readable in security rules
return firebase.firestore()
.doc(".info/connected")
.get({ source: "server" })
.then(
() => {
// read data successfully, we must be online
return true;
}, (err) => {
// failed to read data, if code is unavailable, we are offline
// for any other error, rethrow it
if (err.code === "unavailable")
return false;
throw err;
}
);
}
/**
* A function that attaches a listener to when a connection to Firestore has
* been established or when is disconnected.
*
* This function listens to the non-existant `/.info/connected` document and
* uses it's `fromCache` metadata to **estimate** whether a connection to
* Firestore is currently available.
* **Note:** This callback will only be invoked after the first successful
* connection to Firestore
*
* @param {((error: unknown | null, isOnline: boolean) => unknown)} callback the
* callback to invoke when the isOnline state changes
* @return {(() => void)} a function that unsubscribes this listener when
* invoked
*/
function onOnline(callback) {
let hasConnected = false;
// unlike RTDB, this data doesn't exist and has no function
// must be made readable in security rules
return firebase.firestore()
.doc(".info/connected")
.onSnapshot(
{ includeMetadataChanges: "server" },
{
next(snapshot) {
const { fromCache } = snapshot.metadata;
if (!hasConnected) {
if (fromCache) return; // ignore this event
hasConnected = true;
}
callback(null, !fromCache);
},
error(err) {
callback(err);
}
}
);
}