Flink Scala - метод сравнения нарушает общий договор

2

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

object StreamingDeCP{
  var points: Vector[Point] = _

  def main(args: Array[String]): Unit = {
    val queryPointsVec: Vector[Point] = ... // Read from file
    val pointsVec: Vector[Point] = ...      // Read from file

    val streamEnv: StreamExecutionEnvironment = 
                   StreamExecutionEnvironment.getExecutionEnvironment
    val queryPoints = streamEnv.fromCollection(queryPointsVec)

    points = pointsVec
    queryPoints.map(new StreamingSequentialScan)

    streamEnv.execute("StreamingDeCP")
  }

  final class StreamingSequentialScan 
                    extends MapFunction[Point, (Point, Vector[Point])] {

    def map(queryPoint: Point): (Point, Vector[Point]) = {
      val nn = points
                .map{ _.eucDist(queryPoint) }
                .sorted

      (queryPoint, nn)
    }
  }
}

Класс Point и объект-компаньон:

case class Point(pointID: Long,
                 descriptor: Vector[Float]) extends Serializable {
  var distance: Float = Float.MaxValue

  def eucDist(that: Point): Point = {
    // Simple arithmetic to calculate and set the distance variable
  }
}

object Point{
  implicit def orderByDistance[A <: Point]: Ordering[A] =
    Ordering.by(_.distance)
}

Вот несколько заметок о том, что я пробовал, чтобы определить причину:

  • Утверждение всех значений distance находится между Float.MaxValue и Float.MinValue и не существует значений отрицательного нуля
  • Утверждается, что в одной операции сортировки нет повторяющихся переменных distance (мой вариант использования позволяет это, но я думал, что проверю его на всякий случай)
  • Преобразует значение Float в значение Integer и сортирует по этим значениям вместо
  • Добавлен явный порядок на Point вместо использования implicits
  • Сортировка по уникальному pointID вместо distance, который работает, но бесполезен для контекста этой проблемы.

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

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

  • 0
    Почему у вас есть какие - либо var S в случае класса, почему eucDist возвращает Point вместо скалярного значения, а почему бы вам не просто .sortBy(_.eucDist(queryPoint)) ?
  • 0
    Расстояние не известно при создании экземпляра Point, мне нужно сохранить информацию pointID, и я не знаю семантической разницы между конвейером .sorted я написал, и предложенным .sortBy методом .sortBy .
Показать ещё 1 комментарий
Теги:
flink-streaming

1 ответ

1
Лучший ответ

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

Поскольку ваша Point содержит var s, а те var мутируются в методе map MapFunction, код должен завершиться неудачей, если "Метод сравнения нарушает общий контракт" -exception всякий раз, когда MapFunction выполняется с параллелизмом != 1.

Чтобы избежать каких-либо побочных эффектов внутри функции map, вы можете изменить код следующим образом:

  • Удалите любой var из main, сделайте points неизменным val.
  • Удалите любой вид var из Point
  • Внедрить метод

    def eucDist(other: Point): Double
    

    который просто вычисляет расстояние до другой точки (без каких-либо изменений).

  • Использовать sortBy:

    val nn = points.sortBy(_.eucDist(queryPoint))
    

В качестве альтернативы, если вы хотите избежать повторного вычисления евклидова расстояния несколько раз во время сортировки, предварительно скопируйте расстояния один раз, сортируйте, а затем отбросьте расстояния:

val nn = points.map(p => (p, p.eucDist(queryPoint))).sortBy(_._2).map(_._1)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню