суббота, 9 января 2010 г.

Ошибка в Hunchentoot

Сразу после запуска lisper.ru наблюдались достаточно регулярные зависания (раз в несколько дней): система работала, но http-запросы отрабатывать переставала. Удивительно, но подобное я наблюдал на одном единственном сервере, на других машинах (которые работают в заметно более интенсивном режиме) ничего подобного я не видел. Отладить lisp-процесс на удалённом сервере (в данном случае в Германии) с помощью SLIME почти так же просто как и локальный и при очередном зависании я обнаружил следующую картину:
 23: (SB-IMPL::%WITH-STANDARD-IO-SYNTAX #)
24: (INVOKE-DEBUGGER #)
25: (ERROR SB-BSD-SOCKETS:NOT-CONNECTED-ERROR)[:EXTERNAL]
26: (SB-BSD-SOCKETS:SOCKET-ERROR "getpeername")
27: ((SB-PCL::FAST-METHOD SB-BSD-SOCKETS:SOCKET-PEERNAME (SB-BSD-SOCKETS:SOCKET)) # # #)
28: ((SB-PCL::FAST-METHOD USOCKET:GET-PEER-ADDRESS (USOCKET:STREAM-USOCKET)) # # #)
29: (HUNCHENTOOT::CLIENT-AS-STRING #)
30: ((SB-PCL::FAST-METHOD HUNCHENTOOT:HANDLE-INCOMING-CONNECTION (HUNCHENTOOT:ONE-THREAD-PER-CONNECTION-TASKMASTER T)) ..)
31: ((SB-PCL::FAST-METHOD HUNCHENTOOT:ACCEPT-CONNECTIONS (HUNCHENTOOT:ACCEPTOR)) # # #)
32: ((LAMBDA ()))
Судя по этому стэку вызовов проблема связана с ошибкой при вызове функции getpeername и происходит в функции HUNCHENTOOT::CLIENT-AS-STRING, которая вызывается в HUNCHENTOOT:ACCEPT-CONNECTIONS. Вот код HUNCHENTOOT:ACCEPT-CONNECTIONS:
(defmethod accept-connections ((acceptor acceptor))
(usocket:with-server-socket (listener (acceptor-listen-socket acceptor))
(loop
(when (acceptor-shutdown-p acceptor)
(return))
(when (usocket:wait-for-input listener :ready-only t :timeout +new-connection-wait-time+)
(when-let (client-connection
(handler-case* (usocket:socket-accept listener)
;; ignore condition
(usocket:connection-aborted-error ())))
(set-timeouts client-connection
(acceptor-read-timeout acceptor)
(acceptor-write-timeout acceptor))
(handle-incoming-connection (acceptor-taskmaster acceptor)
client-connection))))))
Вероятно, подобные ошибки являются большой редкостью, но к (не)счастью на одной машине я наблюдаю их постоянно. Для решения проблемы с зависаниями я поступил довольно грубо, изменив данную функцию в RESTAS путём добавление в основной цикл макроса ignore-errors:
(defmethod hunchentoot:accept-connections ((acceptor restas-acceptor))
(usocket:with-server-socket (listener (hunchentoot::acceptor-listen-socket acceptor))
(loop
(when (hunchentoot::acceptor-shutdown-p acceptor)
(return))
(ignore-errors
(when (usocket:wait-for-input listener :timeout hunchentoot::+new-connection-wait-time+)
(handler-case
(hunchentoot::when-let (client-connection (usocket:socket-accept listener))
(hunchentoot::set-timeouts client-connection
(hunchentoot:acceptor-read-timeout acceptor)
(hunchentoot:acceptor-write-timeout acceptor))
(hunchentoot:handle-incoming-connection (hunchentoot::acceptor-taskmaster acceptor)
client-connection))
;; ignore condition
(usocket:connection-aborted-error ())))))))
С тех пор прошло около месяца и никаких зависаний больше не было, так что можно считать, что данный код хотя и грубоват, но таки работает :)

P.S. Описанная проблема слишком сложна для моего английского, поэтому в рассылку hunchentoot я об этом не писал. Было бы здорово, если бы кто-нибудь сделал это вместо меня :)

2 комментария:

  1. Я зарепортил.

    Насчет английского, я не гарантирую что грамматически абсолютно верно, но там люди поймут даже если написать типа "моя делать так и так а программа не работать".

    ОтветитьУдалить
  2. @Anton Vodonosov
    Угу, уже увидел, спасибо большое :)

    ОтветитьУдалить