9 декабря 2016 в 03:38

Разработка → Логические ошибки при работе с типом Buffer, которые очень трудно отыскать

Началось все с того, что мне нужно было подготовить набор байт к отправке через UDP. Разумеется, для этого я выбрал тип Buffer. Код выглядел примерно так:

const sendData = (data, attempts) => {
  attempts && sendDataUDP(data, (err) => {
    if (err) {
      sendData(data, attempts - 1)
    }
  })
}

const id = data.slice(25, 33)

setInterval(() => {
  processId(id)
  if (id.equals(someValue)) {
    sendData(data, 3)
  }
}, 1000)

И в некоторых случаях пакет приходил неверный. Длина пакета при этом была порядка 20 Кб и сам пакет отправлялся частями с подтверждением каждой части.

Что делать?


Разумеется, первое, на что я подумал, — это проблемы с передачей данных по UDP, т.к. пакеты могут теряться или дублироваться. Ввел контрольную сумму. Оказалось, что она не совпадает с checksum перед отправкой.

Начал проверять реализацию передачи пакета по частям. На полную проверку и некоторые дополнения ушел день. Но ошибка так и не убралась. Причем, интересно было то, что все зависело от длины пакета. Попробовал использовать allocUnsafeSlow. Это нисколько не изменило ситуацию.

Случайность — великая вещь!


Параллельно я делал в проекте еще один пункт и случайно заметил, что изменение части буфера (slice) вызывает изменение во всем буфере! Открыл документацию. И пожалуйста!
Note that modifying the new Buffer slice will modify the memory in the original Buffer because the allocated memory of the two objects overlap.

Это значит, что если при отправке пакета запускалась функция processId, то менялся и буфер id, а с ним и весь пакет data!

Мораль: при написании большого проекта после slice делайте copy или Buffer.from, иначе потом найти ошибку будет очень сложно!