Синхронизировать критическую секцию в API для каждого пользователя в JavaScript - PullRequest
2 голосов
/ 23 марта 2020

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

Вот упрощенный (псевдо) код этого:

async function changePic(user, file) {
  // remove old pic
  if (await database.hasPic(user)) {
    let oldPath = await database.getPicOfUser(user);
    filesystem.remove(oldPath);
  }
  // save new pic
  let path = "some/new/generated/path.png";
  file = await Image.modify(file);
  await Promise.all([
   filesystem.save(path, file),
   database.saveThatUserHasNewPic(user, path)
  ]);
  return "I'm done!";
}

Я столкнулся с следующая проблема с ним:
Если пользователь вызывает API дважды за короткое время, возникают серьезные ошибки. Запросы к базе данных и промежуточные функции являются асинхронными, в результате чего изменения первого вызова API не применяются, когда второй API проверяет профиль pi c для удаления. Таким образом, у меня остался запрос filesystem.remove на уже несуществующий файл и неснятое изображение в файловой системе.

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

Есть ли чистый способ добиться этого в JavaScript? Какой-нибудь монитор , как вы знаете из Java, был бы неплох.

1 Ответ

1 голос
/ 10 апреля 2020

Вы можете использовать библиотеку типа p-limit для управления вашим параллелизмом. Используйте карту для отслеживания активных / ожидающих запросов для каждого пользователя. Используйте их идентификатор (который, как я предполагаю, существует) в качестве ключа, а экземпляр лимита - в качестве значения:

const pLimit = require('p-limit');

const limits = new Map();

function changePic(user, file) {
  async function impl(user, file) {
    // your implementation from above
  }

  const { id } = user // or similar to distinguish them

  if (!limits.has(id)) {
    limits.set(id, pLimit(1)); // only one active request per user
  }

  const limit = limits.get(id);
  return limit(impl, user, file); // schedule impl for execution
}

// TODO clean up limits to prevent memory leak?
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...