заново получить и установить срок действия ключа с транзакцией - PullRequest
0 голосов
/ 21 января 2019

Я использую etaty redisscala (https://github.com/etaty/rediscala) клиент. Вот моя функция

private def getVersionTime(db: RedisClient, interval: Long)(implicit ec: ExecutionContext): Future[Long] = {

import akka.util.ByteString
import redis.ByteStringFormatter

implicit val byteStringLongFormatter = new ByteStringFormatter[Long] {
  def serialize(data: Long): ByteString = ByteString(data.toString.getBytes)
  def deserialize(bs: ByteString): Long = bs.utf8String.toLong
}

db.get[Long]("versionTime").map {
  case Some(v) => loggerF.info(s"Retrieved version time ${v}")
    v
  case None => val current = System.currentTimeMillis()
    db.setex[Long]("versionTime", (current / 1000) + interval, current)
    loggerF.info(s"set version time ${current}")
    current
}

}

Вот мой тест. Этот тест вызывает метод выше

it("check with multiple tasks"){
  val target = 10
  val latch = new java.util.concurrent.CountDownLatch(target)
  (1 to target).map{t =>
    getVersionTime(prodDb, 10).map{r => print("\n" + r); latch.countDown()}
  }
  assert(latch.await(10, TimeUnit.SECONDS))
}

Вывод теста

14: 52: 46.692 [pool-1-thread-12] INFO EndToEndITTests - установить время версии 1548062566687 14: 52: 46.693 [pool-1-thread-6] INFO EndToEndITTests - установить время версии 1548062566687 14: 52: 46.693 [pool-1-thread-20] INFO EndToEndITTests - установить время версии 1548062566687 14: 52: 46.692 [pool-1-thread-2] INFO EndToEndITTests - установить время версии 1548062566686 14: 52: 46.692 [pool-1-thread-10] INFO EndToEndITTests - установить время версии 1548062566687 14: 52: 46.693 [pool-1-thread-8] INFO EndToEndITTests - установить время версии 1548062566687 14: 52: 46.692 [pool-1-thread-4] INFO EndToEndITTests - установить время версии 1548062566686 14: 52: 46.692 [pool-1-thread-11] INFO EndToEndITTests - установить время версии 1548062566687 14: 52: 46.692 [pool-1-thread-9] INFO EndToEndITTests - установить время версии 1548062566687 14: 52: 46.692 [pool-1-thread-7] INFO EndToEndITTests - установить время версии 1548062566687

Ожидаемое поведение: установленное время версии должно появиться один раз, а для остальных потоков Время распечатанной версии должно быть напечатано. Я думаю, что мне нужно использовать транзакцию здесь, чтобы получить и setex, завернутый в часы и exec

  private def getVersionTimeTrans(db: RedisClient, interval: Long): Long = {
    import akka.util.ByteString
    import redis.ByteStringFormatter

    implicit val byteStringLongFormatter = new ByteStringFormatter[Long] {
      def serialize(data: Long): ByteString = ByteString(data.toString.getBytes)
      def deserialize(bs: ByteString): Long = bs.utf8String.toLong
    }

    val redisTransaction = db.transaction()
    redisTransaction.watch("versionTime")
    val result: Future[Long] = redisTransaction.get[Long]("versionTime").map {
      case Some(v) => loggerF.info(s"Retrieved version time ${v}")
        v
      case None => val current = System.currentTimeMillis()
        redisTransaction.setex[Long]("versionTime", (current / 1000) + interval, current)
        loggerF.info(s"set version time ${current}")
        current
    }
    redisTransaction.exec()
    val r = for {
      i <- result
    } yield {
      i
    }
    Await.result(r, 10 seconds)
  }

тест

it("check with multiple threads "){
  val target = 10
  val latch = new java.util.concurrent.CountDownLatch(target)
  (1 to target).map{t =>
    Future(getVersionTimeTrans(prodDb, 10)).map{r => latch.countDown()}
  }
  assert(latch.await(10, TimeUnit.SECONDS))
}

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

Ответы [ 2 ]

0 голосов
/ 04 марта 2019

Я решил проблему, используя скрипт LUA

0 голосов
/ 22 января 2019

Рассматривая реализацию rediscala, кажется, что вы не можете использовать оптимистическую блокировку, которую я предложил в исходном ответе (см. Ниже), потому что в rediscala TransactionBuilder не будет отправлять команду WATCH до команды EXEC что делает его довольно бесполезным. На GitHub есть старая закрытая ошибка , которая относится к другому ТАКому вопросу именно с таким сценарием, и ответом является

В rediscala вы не можете читать внутри транзакции, потому что вы будете блокировать клиент для других запросов. Я предлагаю вам попытаться проверить, можете ли вы выполнить проверку в сценарии LUA. (преобразовать транзакцию в скрипт lua)

и несколько месяцев спустя

Он закрыт, потому что не рекомендуется внедрять
Вы должны использовать http://redis.io/commands#scripting

Кажется, что он находится в том же состоянии с 2014 года, и я не думаю, что он когда-либо изменится.


Оригинальный ответ

У меня нет живого Redis для его тестирования, но, глядя на документы транзакций Redis 1031 *, похоже, Redis не поддерживает транзакции в стиле SQL, как вы себе представляете. Он поддерживает атомарные операции, но вы не можете выполнить цикл «запуск транзакции-получение данных-проверка-возможно изменение-принятие». Все команды «get data» будут поставлены в очередь до того, как наконец появится команда EXEC. Это означает, что вы не можете делать какие-либо проверки между get и set внутри одной транзакции.

Если вы посмотрите на раздел « Оптимистическая блокировка с использованием проверки и установки » этого документа, вы увидите, что правильный способ реализации вашего поведения:

  1. Создать транзакцию с watch для ключа
  2. Получить значение за пределами транзакции
  3. Проверьте значение и, если требуется обновление, выполните обновление внутри транзакции. (Не забудьте UNWATCH на случай, если обновления не требуются)
  4. Выполнить транзакцию
  5. Проверьте, не прошла ли транзакция, если это так, повторите весь цикл. Если нет - вы победитель, который написал значение.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...