Обновление документа Firestore несколько раз с помощью функции триггера события перезаписывает или прекращает обновление после нескольких триггеров

Я пытаюсь обновить документ, когда размер группы изображений изменяется, просто чтобы показать прогресс и отслеживать, не удалось ли преобразовать какие-либо изображения.

у нас есть несколько изображений в каталоге, где dir — это идентификатор документа. используя идентификатор документа, мы должны обновить этот документ. мы должны установить поле статуса, статус содержит объекты для преобразованной или не удалось преобразовать информацию. {изображение: путь, обработка: строка}

это будет выглядеть как { ... положение дел : [ имя_файла1:{...}, имя_файла2:{...} ] }

Но когда я загружаю от 5 до 10 изображений, в поле статуса устанавливается только информация о первых 4-5 изображениях. он приостанавливается (или выглядит как пауза или перезаписывается небольшая информация) перед записью последней информации об изображении в статус.

Хотя я пытался просто заменить старые данные новыми в статусе, это означает, что здесь будет только один объект и он будет изменен соответствующим образом. и это работает большую часть времени. я видел редкий случай, когда это тоже не сработало, но в большинстве случаев это работает.

Также пытался создать массив объектов, но при этом также пропускалось несколько записей.

async function updateDoc({filePath, metadata}) {
    const [docId, fileName] = filePath.split('/');
    
    const db = admin.firestore();
    const collectionRef = db.collection(COLLECTION_PACKS).doc(docId);
    const stickersRef = collectionRef.collection(COLLECTION_STICKERS);
  
    let attempts = 0;
    const maxAttempts = 5;

    while (attempts < maxAttempts) {
        try {
            await db.runTransaction(async (transaction) => {

                const updatedStatus = {};
                updatedStatus[fileName] = metadata;

                // Update the document with the new status and progress
                transaction.update(collectionRef, {
                    status: updatedStatus,
                });
            });
            console.info("Updated document for", filePath);
            break; // Exit the loop if successful
        } catch (error) {
            attempts++;
            console.error(`Error updating document (attempt ${attempts}):`, error);
            if (attempts >= maxAttempts) {
                throw error; // Re-throw the error after max attempts
            }
            await new Promise(resolve => setTimeout(resolve, 1000 * attempts)); // Exponential backoff
        }
    }
}

Я также попробовал этот простой фрагмент, который добавляет новый объект в массив статусов, но это также не позволило сохранить интеграцию данных.


      const db = admin.firestore();
      const docRef = db.collection(COLLECTION_PACKS).doc(docId);

      await db.runTransaction(async (transaction) => {
        const docSnapshot = await transaction.get(docRef);
        if (docSnapshot.exists) {

          docRef.update({
            status: FieldValue.arrayUnion(metadata),
          });

        }
      });

Будь то вложенные объекты или массив объектов, любой из них приемлем.

Обновлено: Я наблюдал, как данные перезаписываются немногие объекты вставляются раньше и позже быстро заменяются новыми объектами. на несколько секунд это выглядит так

status : [
   object1, object2
]

тогда это становится

status : [
  object3, object4, object5
]

объект, который быстро вставляется при ранних триггерах, заменяется позже, и кажется, что новое триггерное событие не знает об этих вставленных данных. это расовая проблема.

Дополнительная деталь кода: вот как вызывается метод функции триггера события updateDoc()

exports.resize = onObjectFinalized({ 
    bucket:'sticker-app-2ad48', 
    region:'us-central1', 
    timeoutSeconds: 450, 
    memory:'1GiB',
    cpu:2 
  
  },
    transform
)

async function transform(event){
    ...
    let  metadatainfo = await bucket.file(filePath).getMetadata()[0];
    
    // Early return; 
    if (metadatainfo?.metadata?.process === "done"){
        console.info("previously done processing  | ",fileName)
        return; 
    }

    try {
        if (valid){
            console.info("image is valid ✅ |",filePath)
            await updateMeta({filePath, valid:true, process:"done"});
            return;
        }

        ...  // invoke image processing with sharpjs
        
        if (buffer?.length > 0){
            // save
            log("✔️ transformed ", fileName)
            await bucket.file(`${filePath}`).save(buffer);
            await updateMeta({filePath, valid:true, process:"done"});
            
        }else{
            // leave image data as it is. set metadata
            log("❌ failed to transform  ", fileName)
            await updateMeta({filePath, valid:false, process:"done"});
        }

        buffer = null;
    } catch (e) {
        console.error('Error_> ', e)
        await updateMeta({filePath, valid:false, process:"done", error: e.message});
    } 
}

async function updateMeta({filePath, process, valid, error}){
    // console.info("updateMeta ",filePath)
    let metadata = { process, valid }
    if (error) { metadata.error = error; }
    await bucket.file(filePath).setMetadata({
        metadata: metadata
    })

    await updateDoc({ filePath, metadata})
}

Обновлено: добавление кода на стороне клиента, который может быть причиной проблемы. Код клиентской стороны:

const promises = selectedFiles.map(..) => fetch(`${baseUrl}/upload`, ...) )

// Uploads images which triggers the onObjectFinalized 
// to prcoess and add the info to the doc
Promise.all(promises)
.then((responses) => {
  const jsonPromises = responses.map((response) => response.json());
  return Promise.all(jsonPromises);
})
.then(uploadedImages=>{
  // structure the stickers objects with download url
  ...  
})
.then(stickerObjects=>{
  ... 
  // Populate subcollection "stickers"
  return fetch(`${baseUrl}/addStickerPack/${docId}`, { 
    method: 'PUT', 
    headers: {
      'Content-Type': 'application/json'
    },
    body: body 
  })

})
.then(result=>{
  console.info("Populated subcollection 'stickers' of the Doc ", {docId})
  ......
})
.catch(...)
.finally(...)

последний блок «тогда», который печатает «заполненную подколлекцию...», помог мне найти основную причину. Когда эта строка печатает поле статуса документа, для которого установлено значение неопределенное, что стирает поле статуса, и после этого журнала все, что добавляется в статус, остается интегрированным без каких-либо проблем. удаление метода выборки предотвращает любые изменения в подколлекции документов, устраняет проблему потери данных. Не знаю, почему это происходит.

вот внутренний код, который создает и обновляет подколлекцию.

app.put('/addStickerPack/:stickerPackId', async (req, res) => {
  try {
    const stickerPackDocId = req.params.stickerPackId;
    const {
      ...
      name,
      publisher,
      stickers
    } = req.body;
    
    const stickerPackData = {
      name,
      publisher,
      ...
      
    };


    const stickerPackRef = admin.firestore().collection(COLLECTION_PACKS).doc(stickerPackDocId);
    await stickerPackRef.set(stickerPackData);
    

      // Create sticker documents within the pack
    const stickerPromises = stickers.map(async (sticker) => {
      const stickerRef = stickerPackRef.collection(COLLECTION_STICKERS).doc();
      const stickerData = {
        name: sticker.name, // Replace with actual image storage logic
        image_file: sticker.image_file, // Replace with actual image storage logic
        emojis: sticker.emojis,
        filePath: sticker.filePath,
        valid:false,
      };
      console.info(sticker.image_file)
      return stickerRef.set(stickerData);
    });
      
    // Wait for all sticker writes to complete
    await Promise.all(stickerPromises);
    console.info('Sticker pack added successfully:', stickerPackData);
    const stickerPackId = stickerPackRef.id;
    res.status(201).send({message:'Sticker pack added successfully', id:stickerPackId});
  } catch (error) {
    console.error('Error adding sticker pack:', error);
    res.status(500).send('Internal Server Error');
  }
});
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Использование db.runTransaction() не требуется, поскольку вы не получаете более старых значений для использования в обновлении, поэтому это просто добавляет больше ненужной обработки.

Если ваша структура данных на самом деле { ... status : { filename1:{...}, filename2:{...} } } (обратите внимание, что я заменил квадратные скобки фигурными скобками, я полагаю, вы имели в виду объект, а не массив), то вы можете просто обновить вложенный путь без использования транзакции. Повторные попытки также не требуются:

// this is a docRef, not a colRef as in your example
const docRef = db.collection(COLLECTION_PACKS).doc(docId);
const update = {
  [`status.${fileName}`]: metadata,
};
await docRef.update(update);

===== Обновлено:

Просто чтобы объяснить дальше, когда вы говорите:

Но когда я загружаю от 5 до 10 изображений, в поле статуса устанавливается только информация о первых 4-5 изображениях. он делает паузу перед записью информации о последнем изображении в статус.

Я не думаю, что это остановится. Я считаю, что у вас состояние гонки. Обратите внимание, что вы перезаписываете весь объект status в каждом файле. Это означает, что если файлы запускаются в этом порядке 1-2-3-4, но завершают обработку 1-2-4-3, вы получите информацию, которая была у вас в третьем файле, а не в последнем. Это не соответствует коду, который вы показываете, но, судя по тому, что вы говорите, это возможно, если вы намеревались получить весь объект status перед обновлением.

===== Обновлено еще раз:

Что касается последнего добавленного вами кода, который используется на стороне клиента: этот код перезаписывает весь документ, в результате чего свойство status становится неопределенным. Вот почему вы добавляете 1,2,очистить от клиента,3,4,5 и в итоге получаете только 3,4,5.

Измените строку, которая устанавливает документ, очищая остальную часть документа. Вместо:

await stickerPackRef.set(stickerPackData);

скорее используйте

await stickerPackRef.set(stickerPackData, {merge:true});
OR
await stickerPackRef.update(stickerPackData);

Спасибо за ответ,. так что, если я использую массив, все будет в порядке. в моем существующем коде я также попытался поместить новый элемент в массив статуса и столкнулся с той же проблемой. Я использовал вложенный объект, потому что замена объекта каждый раз без сохранения предыдущих значений в большинстве случаев работает нормально (иногда этот метод также пропускает последние 1 или 2 записи).

CrackerKSR 30.06.2024 18:19

окей, я обнаружил, что это не пауза, а перезапись. возможно, любой вызванный экземпляр не знает о новых данных в массиве/вложенных объектах. он принимает пустой и помещает данные и обновляет документ, который фактически заменяет ранее добавленные данные.

CrackerKSR 01.07.2024 04:41

изображения, размер которых не изменяется или не становится маленьким, поэтому их выполнение не занимает много времени, сначала они устанавливают данные, но позже они заменяются новыми изображениями, поэтому я видел, как он помещает 3 объекта, а затем заменяет их 7 новыми объектами, поэтому мы пропустили первые 3 изображения. поэтому я обновил заголовок вопроса. он не останавливается, а всегда устанавливает документ, и я думаю, что проблема заключается в перезаписи.

CrackerKSR 01.07.2024 05:22

@CrackerKSR "так что, если я использую массив, все будет в порядке". На самом деле, нет. Если вы используете массив status объектов file-status, вам необходимо использовать транзакцию (чтобы получить текущий массив status и добавить свой собственный file-status объект). Использование объекта status (вместо массива) решает эту проблему: вам не понадобится транзакция, и вы можете обновлять по вложенному пути, как в примере в моем ответе (await docRef.update({ 'status.whateverName': whateverStatus });).

maganap 02.07.2024 11:35

Но все же у меня возникает та же проблема с вложенным объектом без транзакции. Я видел журнал на стороне клиента, который прослушивал изменения документа с помощью onSnapshot. он добавляет 1 или 2 объекта, а затем показывает неопределенное значение. после этого будут добавлены результаты объектов. поэтому мы потеряли значения перед установкой неопределенного. и заметил, что это происходит, когда я получаю ответ от метода выборки, который добавлял информацию в подколлекцию того же документа. Я удалил его, и теперь он работает нормально. Я добавил код, который отправляет изображения на сервер, а также обновляет подколлекцию документа.

CrackerKSR 02.07.2024 16:29

Так вы говорите, что сейчас это работает? Это был другой процесс очистки значения status?

maganap 02.07.2024 16:40

да. я добавил коды. Кажется, что оператор, который обновляет документ, устанавливает совершенно новые свежие значения для всего документа, которые также стирают поле статуса. Итак, наконец-то нашли причину и устранили проблему. Спасибо за ответ..

CrackerKSR 02.07.2024 18:09

О, я только что увидел. Вы используете set(), который, да, очищает все остальные значения. Вам необходимо либо: A) использовать update(), если вы уверены, что документ уже существует, либо B) использовать set() с merge: true, чтобы другие значения не очищались. Я рад, что вы решили это. Если мой ответ помог, не могли бы вы пометить его как принятый? Спасибо

maganap 02.07.2024 18:20

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