Действительно ли эта сопрограмма не блокирует запись на канал?

Я экспериментировал с попыткой передать большой двоичный объект в браузер из базы данных SQLite без необходимости сначала записывать большой двоичный объект в память и не блокировать сценарий Tcl. Очевидно, что chan copy с -command позаботится об этом (я пробовал с файлом, и он не блокируется), но эта функция не будет работать с incrblob SQLite.

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

Приведенный ниже код передает большой двоичный объект, представляющий собой видеофайл размером 250 МБ, в браузер, поскольку его канал $sock содержит событие, доступное для записи. Это работает, поскольку видео воспроизводится, и каждые 30 секунд или около того событие, кажется, срабатывает, и копируется больше данных.

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

Мои вопросы:

  1. Является ли этот подход разумным? Это минимальный локальный сервер для настольного приложения.
  2. Он действительно сейчас не блокируется? Это довольно сложно протестировать, потому что новые запросы от браузера малы и копируются очень быстро, так что нет времени пытаться запустить другой процесс в то же время. И это само по себе является решением проблемы блокировки: просто блокируйте на очень короткий промежуток времени, чтобы это не было заметно. Но, я хотел бы знать в любом случае. Оба канала настроены на -blocking 0, и я думаю, что сопрограмма должна помочь ему не блокироваться; но я не совсем понимаю это, за исключением того факта, что он возвращает управление вызывающей стороне, которая в данном случае является процедурой без оставшихся операторов для выполнения. Я так понимаю, это означает, что нет необходимости использовать подход after 0 [] с сопрограммой.
  3. Кто-нибудь знает, в чем проблема с incrblob? Дело в том, что его канал принадлежит другому интерпретатору, так что события не попадают в нужное «место»?

Спасибо за рассмотрение моего вопроса.

proc ::REQ::SendBLOB {sock filename type id} {
  dbws eval {select rowid, length(col_blob) as bytes from db.table where id=:id} {
    # Get the BLOB as a (read-only) channel
    set fp [dbws incrblob -readonly db table col_blob $rowid]
    chan configure $fp -translation binary -blocking 0
    chan configure $sock -translation binary -blocking 0
    # Would use chan copy with -command but it will not work with incrblob.
    # chan copy $fp $sock -size $bytes -command [list ::REQ::ChanCopyCleanup $fp]
  }
  coroutine ::coro_incrblob${sock} proxyfcopy $fp $sock $bytes
  chan event $sock writable ::coro_incrblob${sock}
}

proc proxyfcopy {fp sock bytes} {
  while { ![chan eof $fp] } {
    yield
    # Think this 4096 should be at least 5,120,000; for it appears
    # that Firefox requests 60 MB at first load and 5 MB thereafter.
    if { $bytes > 4096 } {
      set size 4096
    } elseif { $bytes > 0 } {
      set size $bytes
    } else {
      chan event $sock writable {}
      close $fp
      return
    }
    set data [read $fp $size]
    chan puts stdout $bytes
    chan puts -nonewline $sock $data
    set bytes [expr {$bytes - [string length $data]}]
  }
}
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Каналы incrblob не поддерживают неблокирующую работу; их базовый дескриптор канала не имеет метода отключения режима блокировки (это поле в записи типа C для типа канала — NULL). Однако запись в большой двоичный объект в любом случае будет выполняться довольно быстро, поскольку она определенно не требует перераспределения пространства — механизм доступа к добавочному большому двоичному объекту не может изменить размер большого двоичного объекта — или фиксации транзакции (в режиме автоматической фиксации операция фиксации происходит, когда вы закрываете канал; в противном случае это происходит, когда вы COMMIT явно, конечно). Это немного похоже на работу со стандартным файлом; они технически позволяют вам установить неблокирующий режим, но в этом нет смысла, потому что операционная система фактически игнорирует флаг в этой ситуации (или, скорее, это больше похоже на то, что все файлы являются неблокирующими, за исключением случаев, когда в сетевой файловой системе, когда вы получаете все равно заблокирован, потому что у сетевых файловых систем странная семантика). Для каналов только для чтения incrblob они будут эффективно выполняться из памяти после того, как БД настроит все и, следовательно, очень и очень быстро.

Поскольку вы также имеете дело с сетевыми сокетами, они действительно поддерживают неблокирующий режим. В частности, сокеты доступны для записи, когда их выходной буфер на уровне ОС не заполнен.

Реализация chan copy требует, чтобы сторона чтения была неблокирующей для асинхронной операции. Вы не можете сделать это здесь, но вы все равно можете использовать синхронный chan copy, чтобы избежать копирования данных на более высоких уровнях среды выполнения Tcl. Если вы используете фиксированные передачи с этим (1444, вероятно, является хорошим предположением — это 1500 Ethernet MTU минус 54 байта служебных данных для заголовка Ethernet, заголовка IP и заголовка TCP, округленного до размера слова — но больше тоже может быть хорошо) потому что вы в конечном итоге имеете дело с потоковой моделью при выполнении TCP, и ОС делает некоторые вещи для вас; вам нужно будет измерить), тогда вы должны получить разумную эффективность. Перемещайте один из них всякий раз, когда выходной канал доступен для записи.

Ничто не мешает @Gary использовать синхронный chan copy в выделенных потоках записи. (1) передать канал incrblob и sock в поток (например, взятый из пула) с помощью thread::transfer и (2) выполнить chan copy в этом потоке. Учитывая «разумную эффективность», на которую указал Донал, будет достаточно небольшого пула потоков записи.

mrcalvin 16.05.2023 23:10

Спасибо. При использовании синхронного chan copy является ли целью фиксированного размера передачи 1444 байта «защита» от браузера, заполняющего свой буфер при частичной передаче, оставляя некоторые байты незаписанными из копии размером 1444 и приводя к блокировке Tcl? При экспериментах с большими размерами это происходило временами; но в 14:44 я не мог наблюдать, как это происходит.

Gary 17.05.2023 02:54

1444 был предположением о том, что приведет к эффективной отправке пакета, причем предположение было основано на том, насколько реально работает сеть (о чем я, к сожалению, хорошо осведомлен). Большие значения также могут работать нормально; там много сложностей. Использование доступных для записи событий, чтобы узнать, когда делать копию, означает, что вы будете заполнять сетевые буферы ОС и оборудования, но, вероятно, не будете блокировать. Эти вещи определенно нуждаются в настройке.

Donal Fellows 17.05.2023 10:56

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