Posts

  • How fast are Unix domain sockets?

    Warning: this is my first post written in English, after over five years writing only in Portuguese. After reading many technical articles written in English by non-native speakers, I’ve wondered: imagine how much information I would be missing if they wrote those posts in French or Russian. Following their examples, this blog can also reach a much wider audience as well.

    It probably happened more than once, when you ask your team about how a reverse proxy should talk to the application backend server. “Unix sockets. They are faster.”, they’ll say. But how much faster this communication will be? And why a Unix domain socket is faster than an IP socket when multiple processes are talking to each other in the same machine? Before answering those questions, we should figure what Unix sockets really are.

    Unix sockets are a form of inter-process communication (IPC) that allows data exchange between processes in the same machine. They are special files, in the sense that they exist in a file system like a regular file (hence, have an inode and metadata like ownership and permissions associated to it), but will be read and written using recv() and send() syscalls instead of read() and write(). When binding and connecting to a Unix socket, we’ll be using file paths instead of IP addresses and ports.

    In order to determine how fast a Unix socket is compared to an IP socket, two proofs of concept (POCs) will be used. They were written in Python, due to being small and easy to understand. Their implementation details will be clarified when needed.

    IP POC

    ip_server.py

    #!/usr/bin/env python
    
    import socket
    
    server_addr = '127.0.0.1'
    server_port = 5000
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind((server_addr, server_port))
    sock.listen(0)
    
    print 'Server ready.'
    
    while True:
        conn, _ = sock.accept()
        conn.send('Hello there!')
        conn.close()
    

    ip_client.py

    #!/usr/bin/env python
    
    import socket
    import time
    
    server_addr = '127.0.0.1'
    server_port = 5000
    
    duration = 1
    end = time.time() + duration
    msgs = 0
    
    print 'Receiving messages...'
    
    while time.time() < end:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((server_addr, server_port))
        data = sock.recv(32)
        msgs += 1
        sock.close()
    
    print 'Received {} messages in {} second(s).'.format(msgs, duration)
    

    Unix domain socket POC

    uds_server.py

    #!/usr/bin/env python
    
    import os
    import socket
    
    server_addr = '/tmp/uds_server.sock'
    
    if os.path.exists(server_addr):
        os.unlink(server_addr)
    
    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.bind(server_addr)
    sock.listen(0)
    
    print 'Server ready.'
    
    while True:
        conn, _ = sock.accept()
        conn.send('Hello there!')
        conn.close()
    

    uds_client.py

    #!/usr/bin/env python
    
    import socket
    import time
    
    server_addr = '/tmp/uds_server.sock'
    
    duration = 1
    end = time.time() + duration
    msgs = 0
    
    print 'Receiving messages...'
    
    while time.time() < end:
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        sock.connect(server_addr)
        data = sock.recv(32)
        msgs += 1
        sock.close()
    
    print 'Received {} messages in {} second(s).'.format(msgs, duration)
    

    As we can see by those code snippets, both implementations are close to each other as possible. The differences between them are:

    • Their address family: socket.AF_INET (IP) and socket.AF_UNIX (Unix sockets).
    • To bind a process using socket.AF_UNIX, the socket file should be removed and created again if it already exists.
    • When using socket.AF_INET, the socket.SO_REUSEADDR flag have to be set in order to avoid socket.error: [Errno 98] Address already in use errors that may occur even when the socket is properly closed. This option tells the kernel to reuse the same port if there are connections in the TIME_WAIT state.

    Both POCs were executed on a Core i3 laptop running Ubuntu 16.04 (Xenial) with stock kernel. There is no output at every loop iteration to avoid the huge performance penalty of writing to a screen. Let’s take a look at their performances.

    IP POC

    First terminal:

    $ python ip_server.py
    Server ready.
    

    Second terminal:

    $ python ip_client.py
    Receiving messages...
    Received 10159 messages in 1 second(s).
    

    Unix domain socket POC

    First terminal:

    $ python uds_server.py
    Server ready.
    

    Second terminal:

    $ python uds_client.py
    Receiving messages...
    Received 22067 messages in 1 second(s).
    

    The Unix socket implementation can send and receive more than twice the number of messages, over the course of a second, when compared to the IP one. During multiple runs, this proportion is consistent, varying around 10% for more or less on both of them. Now that we figured their performance differences, let’s find out why Unix sockets are so much faster.

    It’s important to notice that both IP and Unix socket implementations are using TCP (socket.SOCK_STREAM), so the answer isn’t related to how TCP performs in comparison to another transport protocol like UDP, for instance (see update 1). What happens is that when Unix sockets are used, the entire IP stack from the operating system will be bypassed. There will be no headers being added, checksums being calculated (see update 2), encapsulation and decapsulation of packets being done nor routing being performed. Although those tasks are performed really fast by the OS, there is still a visible difference when doing benchmarks like this one.

    There’s so much room for real-world comparisons besides this synthetic measurement demonstrated here. What will be the throughput differences when a reverse proxy like nginx is communicating to a Gunicorn backend server using IP or Unix sockets? Will it impact on latency as well? What about transfering big chunks of data, like huge binary files, instead of small messages? Can Unix sockets be used to avoid Docker network overhead when forwarding ports from the host to a container?

    References:

    Updates:

    1. John-Mark Gurney and Justin Cormack pointed out that SOCK_STREAM doesn’t mean TCP under Unix domain sockets. This makes sense, but I couldn’t find any reference affirming nor denying it.
    2. Justin Cormack also mentioned that there’s no checksumming on local interfaces by default. Looking at the source code of the Linux loopback driver, this seems to be present in kernel since version 2.6.12-r2.
  • Contribuições com Software Livre em Maio de 2016

    Uma prática muito comum que observo há algum tempo em comunidades de software livre são relatórios mensais sobre suas próprias contribuições. A ideia é informar sobre o que foi feito recentemente, assim como atrair pessoas que possam se interessar em projetos nas mesmas áreas para trabalharem comigo. Sendo assim, seguem minhas contribuições no mês de Maio de 2016.

    Debian

    bootstrap-vz

  • Contribuições com Software Livre em Abril de 2016

    Uma prática muito comum que observo há algum tempo em comunidades de software livre são relatórios mensais sobre suas próprias contribuições. A ideia é informar sobre o que foi feito recentemente, assim como atrair pessoas que possam se interessar em projetos nas mesmas áreas para trabalharem comigo. Sendo assim, seguem minhas contribuições no mês de Abril de 2016.

    Debian

    • Apliquei para o processo de me tornar um Debian Maintainer, obtendo o apoio de Antonio Terceiro (que eu havia pedido) e Charles Plessy (o que foi uma agradabilíssima surpresa). A ideia é assumir uma posição “mais oficial” junto ao projeto e, quem sabe, daqui uns tempos me tornar um Debian Developer (com um e-mail @debian.org).
    • Assumi os bugs ITP (Intent to Package) #790611 e #790612, do Grip e do Path-and-Addresss (dependência do Grip), respectivamente. Gustavo Panizzo os havia criado no meio do ano passado, mas não teve tempo de terminar o empacotamento. Perguntei se poderíamos trabalhar juntos nisso e ele me permitiu assumir a responsabilidade.
    • Corrigi alguns problemas no empacotamento do cloud-utils, como o copyright de uma man page que havia ficado faltando e me foi apontado pelo Chris Lamb. O upload foi patrocinado pelo Antonio Terceiro, que tem me ajudado bastante com o cloud-utils.
    • Corrigi o bug #820607, reportado pelo Olivier Berger, relacionado ao changelog do upstream incluído no pacote do bootstrap-vz estar praticamente vazio.
    • Corrigi um hífen não-ASCII na página DebianMaintainer da Wiki do Debian. Pode parecer bobo, mas isso fez com que minha mensagem de aplicação tivesse sua assinatura invalidada na interface web da lista de e-mail.
    • Desencorajei a entrada de um pacote, o series, no bug RFS (Request for Sponsorship) #820733. Infelizmente se trata de um software muito recente, praticamente utilizado apenas pelo autor. Não é o tipo de software que deve ser empacotado para o Debian.
    • Realizei diversas (muitas mesmo) melhorias no pacote do bootstrap-vz. Se tiver trabalhado 30h só com isto ainda foi pouco. A mais importante é a criação do pacote bootstrap-vz-doc, com a documentação em HTML disponível para leitura offline.
    • Reforcei, no bug #788527, o pedido para que o pacote python2-pyro4 (dependência do bootstrap-vz) fosse atualizado, o que foi rapidamente atendido pelo Laszlo Boszormenyi.
    • Reportei alguns pequenos problemas com o “collab-maint” (repositório para manutenção colaborativa de pacotes), como certificado HTTPS expirado e falta de permissões para configurar corretamente um repositório. Alexander Wirt e Mattia Rizzolo me ajudaram a resolver estes problemas.
    • Reportei o bug #820020, solicitando a atualização do pacote python-responses, dependência dos testes do python-path-and-address, o que foi atendido (muito) rapidamente pelo Ondrej Novy.
    • Reportei o bug #820685, incluindo alguns patches com melhorias no o empacotamento do zbackup.
    • Resolvi o bug #821175, relacionado a um conflito com o diretório de configurações do grip. O que acontece é que um pacote do GNOME CD Ripper tinha o mesmo, mas mesmo não faz mais parte do Debian desde 2009 (e foi abandonado pelo upstream em 2005). Sendo assim, não havia nada a ser feito.
    • Revisei o pacote python-django-gravatar2, referente ao bug RFS #819681. Encontrei alguns problemas, mas o Pierre-Elliott resolveu a maioria deles.
    • Revisei a imagem do Debian Jessie 8.4 criada para o Amazon EC2 pelo James Bromberger. Encontrei alguns pequenos problemas, mas nada grave.
    • Revisei o pacote netsed, referente ao bug RFS #821236. O pacote é mantido pelo Mats Erik Andersson há mais de 5 anos, então não havia nada muito grave a ser corrigido.
    • Revisei o pacote python-hashids, referente ao bug RFS #820900. O empacotamento foi feito por um antigo DD, Edward Betts, então não tive nada a acrescentar.
    • Revisei o pacote roadfighter e encontrei pouquíssimos detalhes a serem corrigidos, devido ao grande trabalho do Carlos Donizete Froes.
    • Submeti o bug RFS #820029 do grip, atendido pelo Mattia Rizzolo.
    • Submeti o bug RFS #819773 do python-path-and-address, também atendido pelo Mattia Rizzolo.
    • Tive meu bug RFS #819289 do pythonpy, que havia submetido no final de março, atendido. O upload foi patrocinado pelo Dmitry Shachnev.

    bootstrap-vz

    Path-and-Address

  • Desligando a tela ao bloquear o Xfce

    Como quem acompanha o blog sabe, utilizo exclusivamente Linux no desktop há quase quatro anos. Optei por muito tempo pelas versão estáveis do Debian (Squeeze/Wheezy na época) no notebook, passando a utilizar o Xubuntu (versões LTS, 12.04 e 14.04, com suporte extendido, mas de apenas três anos invés de cinco, diferente do Ubuntu) no computador de trabalho. Considerando a usabilidade do Xubuntu maior do que a do Debian com Xfce, foi natural que passasse a utilizar o primeiro também no meu notebook pessoal.

    Questiono a usabilidade do Debian porque é necessário configurar manualmente muitas das coisas que já vem habilitadas por padrão no Xubuntu. Coisas que podem parecer bobas, como o “clique com toque” e “clique com o botão direito, com toque de dois dedos” no touchpad fazem muita diferença. E, tendo voltado pro Debian (Jessie, a atual versão estável) recentemente, um destes detalhes voltou a me irritar profundamente: o fato da tela não ser desligada quando bloqueio o sistema (exigindo senha para utilizá-lo novamente).

    Como não poderia deixar de ser, se você procurar por isso encontrará soluções esdrúchulas, como a recomendação de se criar um script que chama o comando de salvar a tela, espera um tempo e chama outro comando para desligá-la depois. E o pior de tudo, é que como se já não bastasse você precisar de um script para fazer isto em pleno ano de 2016, ele simplesmente não funciona! Por algum motivo a tela liga sozinha pouco tempo depois.

    Felizmente, há uma solução simples, ainda que não tão intuitiva, para o problema. Por padrão, o Debian (pelo menos com Xfce) utiliza o XScreenSaver para gerenciamento de proteção de tela, e ele é o responsável por desligá-la após bloquear a sessão. Acessando sua configuração através do menu “Settings -> Screensaver” (ou acessando a opção “Screensaver” do “Settings Manager”), é necessário escolher as seguintes opções:

    • Mode: Blank Screen Only;
    • Lock Screen After: esta opção não é obrigatória e não irá influenciar quando a tela for bloqueada propositalmente, apenas após o computador ter ficado inativo pelo tempo configurado nas opções anteriores.

    E o problema todo está nesta última tela. Note que há a opção Quick Power-off in Blank Only Mode, que é exatamente o que estávamos procurando. Entretanto, a mesma depende da opção Power Management Enabled estar marcada, muito embora ela fique disponível para ser ativada/desativada, estando a opção anterior selecionada ou não! Meu professor de “Interface Homem-Máquina”, Frederico Bida, provavelmente infartaria vendo uma coisa dessas.

    Como pode ser percebido, deixei as demais opções de energia em 1440 minutos (24 horas), simplesmente porque não quero que o XScreenSaver controle quando o computador deve ser suspenso/desligado, já que isto fica a cargo do gerenciador de energia do Xfce. A intenção é que ele se preocupe apenas com o que realmente interessa, que é o fato de desligar corretamente a tela quando a mesma for bloqueada.

  • O lamentável estado dos softwares Freeware

    Sempre fui aficionado em conhecer e experimentar softwares novos. Ainda criança, um dos meus passatempos preferidos era passar o fim de semana (em uma conexão discada de 14.4 Kbps) navegando entre as seções, principalmente de Rede e Internet e Utilitários, do Superdownloads - que pasmem, existe desde 1998. Não demorou para que eu descobrisse em quais condições os softwares eram ali distribuídos: havia versões de demonstração (Demo), assim como as pagas porém gratuitas para testar por tempo limitado (Shareware). Destas, a categoria que mais me atraia era a dos gratuitos sem limitação de tempo ou funcionalidade: os Freeware.

    Foi nesta época que conheci softwares como o mensageiro multi-protocolo Miranda IM (que na época provavelvemente ainda se chamava Miranda ICQ, nome que foi mudado no final de 2002). Ele não é freeware e sim opensource, mas vale a pena citá-lo por conta de um bug extremamente curioso (e irritante) das primeiras versões: às vezes, quando utilizado com o protocolo do MSN (mais tarde chamado de Windows Messenger), ele perdia a habilidade de enviar mensagens mas as continuava recebendo. Se estivesse, por exemplo, discutindo com alguém, a pessoa poderia te xingar repetidamente e te chamar de covarde… E você nada poderia fazer.

    Nestas incursões no mundo dos softwares freeware, conheci muitas alternativas bacanas (todas para Windows), como o Resource Hacker, que permite editar arquivos executáveis, e o Spybot Search & Destroy, removedor de adwares/spywares. Tenho um grande carinho pelos softwares da AnalogX, em especial, por terem criado exemplares pequenos porém de grande utilidade, como o AnalogX Capture, AnalogX Proxy e AnalogX Vocal Remover. O mais importante a ser destacado aqui é que estes softwares citados são exceções, por terem se mantido funcionais e sem pegadinhas através do tempo.

    O grande problema com os softwares freeware, que pode não estar aparente, é seu modelo de negócio. Se é um produto grátis, sem custo para o usuário final, quem banca seu desenvolvimento, distribuição e manutenção? É justamente ao se tentar gerar receita com estes programas, normalmente depois de já serem conhecidos pelo público, é que o processo desanda. O μTorrent, por exemplo, tentou adicionar anúncios e chegou a instalar um minerador de criptomoedas, em uma tentativa desesperada de gerar receita.

    Alguns casos mais graves, como o do BS Player, chegaram a impossibilitar sua utilização caso o usuário removesse os adwares que acompanhavam o instalador. A situação fica ainda mais crítica quando isto se torna tão corriqueiro que as pessoas simplesmente se conformam, como foi o caso de um amigo e colega de trabalho. Ele me disse que usava o KMPlayer, mas que parou quando o instalador passou a adicionar um monte de porcarias indesejadas. E qual foi sua reação? Passar a usar outro player gratuito “enquanto ele não começar a adicionar coisas inúteis ao instalador”.

    A parte reconfortante desta história toda é que há uma alternativa: utilizar software livre. Pode parecer clichê ou “coisa de quem usa Linux”, mas não é. O excelente player de vídeo VLC (o “Chuck Norris” dos media players), por exemplo, roda em praticamente qualquer sistema operacional existente. Sem ter de ficar instalando incontáveis codecs (nenhuma saudade do K-Lite Codec Pack), o VLC toca praticamente qualquer arquivo de mídia existente (inclusive imagens ISOs sem precisar montá-las). E se alguém algum dia tentasse incluir anúncios ou outros softwares inúteis no instalador, com certeza outra pessoa empacotaria e distribuiria uma versão “limpa” do mesmo.

Subscribe via RSS