domingo, 10 de agosto de 2014

Steve Yegge's Rantings book

Those who know me well, know that I'm a bit of a Steve Yegge fanboy. That's true, I love reading Steve's mean jokes and I keep coming back to those long blog posts. That dude's writing has been a great influence to me. I've even started to write long posts myself, in a sort of cargo-cult-steve-yegge-wannabe behavior. I'm such a fanboy. Steve would probably feel embarrassed for me.

Anyway, I discovered a while ago that he has quietly released a book called A Programmer's Rantings: On Programming-Language Religions, Code Philosophies, Google Work Culture and Other Stuff, which is a collection of some of his articles put together with a few notes adding a bit more context to each article. It was published by HyperInk, which seems to be a cool company. I bought the ebook from their website and it offered me to send as a gift to a few friends, so it was a nice deal.

Cover of Steve Yegge's Rantings book

And the book is quite a journey. You get to read some of the first essays Steve wrote when he was pretty unknown (just another bright young fellow at Amazon) and also his latest rants posted on Google Plus, including the one he made public accidentally and it ended up making him kind of famous outside of the programming circles too.

I enjoyed reading this book a lot, although I suppose it would be expected for me say that, what with me being a fanboy and all. That being said, it's a quick read and lot's of fun -- highly recommended! Although it amounts to more than two hundred pages it still does not make half of the stuff on his blogs. So, if you like the book, you'll probably want to check out the blogs. Or vice-versa, whatever.

segunda-feira, 16 de junho de 2014

Tips on Performance and Monitoring of Java Applications

I cleaned up my desk this weekend and ended up finding some notes I wrote down some time ago when I attended a course about monitoring JBoss application servers. I haven't been doing much related to Java Web development lately, but I'm going to try to make a quick summary here because: 1) then I can safely throw away the paper with the notes, and 2) I think some of the tips can still be useful in the context of many long running JVM processes.

Memory and GC load

So, the most important thing to monitor in a long running JVM is arguably the garbage collection stats. When some JVM is misbehaving, the first thing you want to check is how much of the heap is being used and how often the full GC is being called.

One quick tip to reduce GC load is to set the initial heap size to the same amount of the maximum heap size, namely, you should use the same value for the -Xms and -Xmx JVM options. When you do this the JVM will allocate the maximum space it will ever use, right from the the start. This way you avoid memory reallocation for the heap and some GC calls, and can even sometimes speedup the startup of an application, it is usually the first thing I do when I install a Java desktop app like Eclipse or Intellij IDEA.

There are some nice articles about garbage collection in Java available, it's a fairly big topic and I don't know much beyond the basics to survive. You definitely want to know that full GC stops are bad and, although you can't avoid them completely, you want to keep them to a minimum so your application won't keep slogging.

Full GC stops happen when the area of memory reserved to objects that are "old" (namely, objects that have been around for most of the time and therefore are probably going to be around in the future) reaches its size limit. That means that whatever application code was running will be stopped and only the GC will execute for a moment, until some space is freed and your application code can execute again (or you'll be the misfortunate heir to an OutOfMemoryError).

As it happens, tuning the garbage collection is very specific to the type of application you're running. The JVM can use different garbage collection algorithms, and you may want to twiddle with them a little bit to see what works best. For your typical Java Web application, it's usually better to run several machines with moderate memory in a cluster than run all in one big machine with several GiB of memory due to how the GC works. However, your mileage may vary, so your best bet is to measure and monitor what's happening with the heap.

The number of threads vs backlog tradeoff

Now, in the case of Web applications, the second most important thing you want to watch is the busy threads count. Usually, it represents the number of requests being handled at a given moment. There is an useful tradeoff to be aware of that has to do with how these things happen inside the Web container, so bear with me for a little bit with the inaccurate description.

Whenever your application server (or whatever it is you're using to deploy your Java Web app) gets a new request, it tries to respond as soon as possible creating a new thread to handle it, right? Then, when the configured maximum number of threads is reached, all the new requests are kept on a queue (sometimes called backlog or acceptCount), waiting until one of the current busy threads finishes so that the server can then get the next request from the queue and use another thread to handle it.

Well, okay, it's not reaaally like that, I think what really happens is that each new request always goes to the backlog queue and there are some threads responsible for request processing that keep getting work from the backlog, but I thought it would be a bit easier to explain that way.

woman thinking about how best to tune her Tomcat installation

But here is the little secret: it's usually much easier for the operating system to manage queues than attend threads. So, often enough it's better to let the queue get big than to increase the maximum number of threads for your system to handle. Threads are often competing for resources, increasing its number may even hurt performance instead of improve it, specially when you have a peak load.

Of course the best thing to do will depend on the application you are serving and the load patterns in your server, so you need to measure it and experiment for a bit to figure out what numbers work best for you.

So, if there is any way to wrap-up all this, it would be: measure it, measure it, measure it! Different types of long-running JVM applications will have different effects on your system, so it's important to have data to make good decisions. Measuring the heap stats to see how the GC is doing and the number of busy threads to see the patterns in request handling (in the case of Web applications) is a good start.

Thanks for the review, José Ricardo and Valdir Stumm!

sexta-feira, 13 de junho de 2014

So, here is a story

Soon after graduating, I went to work as a software developer for a big public company at Brazil. This company is responsible for a huge part of the country's IT infrastructure, and they try to pass on a reputation of being innovative and pro open source, which excited me a lot. I thought: "They promote Linux and free software... Heck, their website is built with Plone. I'll get to do cool stuff in Python all day!" I know it's naive, but that was my thinking back then.

So, initially I thought I had won the lottery. But that feeling didn't take much long to dissipate. What I ended up finding was NOT a place where engineers are working together on great projects, embracing free software and pushing out good stuff to the world.

That company is weird. And the biggest problem for me was: I was young and inexperienced, a child basically, surrounded mostly by other children. You see, most developers there are pretty smart people. But they don't have much say in the decisions about the software development, arguably the thing they know best.

The software development department is run by mostly traditional managers who don't seem to understand how software development actually works. It seems that it has been on deficit for quite some time, though nobody cares too much -- hey, the taxpayers are still funding.

So, those managers wanted the Big Ole' Software Factory. You know, those imaginary places with a human machinery resembling an assembly line that if you set up it just right and feed it with the right input, you'll get good software on the other end.

Well, what they got is a bunch of talented people managed in a very paternalistic style. And that leads to pain for the developers who, being treated like children, end up behaving a lot like children (myself included, I am ashamed to say).

I don't need to tell you that they don't produce great software. Well, when they get to ship something, that is. But I'm not talking about the quality of their software right now.

Right now, my point is: this kind of place is not a good place for someone trying to grow into a good, mature engineer.

You need a healthy environment to build up maturity and excellence. And an environment is not healthy if you are encouraged to simply follow orders and not try to challenge the status quo. Because you won't learn to make better decisions if you are not to be trusted to decide very basic things, like what software to install in your development machine.

People at that company end up having serious trust issues with their bosses and colleagues, because of the unhealthy environment. The funny thing is, I think most people are there because they like the job security, but they probably don't get to feel much safe there. At least, not the majority of the people I knew there.

I wish I had read the wonderful Team Geek book back then, and I wish everybody there would read that book now and give it some serious thought. That would bring some light to several of the issues they have there, and maybe the place would be better than it is.

I remember getting there full of energy, eager to learn and do stuff. Somewhere along the way, I kind of lost the will. I remember thinking like: "Okay, I give up, I don't know how to deal with this. I'll remain as a mediocre performer, as that seems what they want around here. I will focus on honing my guitar skills, so that I won't want to kill myself."

Well, I've moved on to greener pastures some time ago, not before another experience at another not-so-great government institution. And although I'm not young anymore, I think I've managed to stay curious and I'm still eager to learn and do stuff. That's the important thing, right?

But sometimes, looking back how much I have improved after leaving that place, I can't help but wonder what if I never had got there in the first place? Would I be able to enjoy the working, learning and growing as I do now? Or would I be like so many others out there, whining and thinking I would be happier enjoying the job security of a public employee?

I guess I'll never know. It's probably better thinking it would be the latter, though. That way I have no regrets.

Thanks to my friends who reviewed this.

sábado, 31 de maio de 2014

Failing to finish a book

Okay, here is a book I've been trying to read for almost two months already and barely get to the half of it. Python for Data Analysis has left me with mixed feelings. Of all the O'Reilly books I've got until now, this is probably the one in worst shape.

It seems that the author (Wes McKinney, the author of the Pandas library -- a great guy, no doubt, and that obviously possesses an extensive technical knowledge) did not spend much time trying to get the book more digestible. The Pandas library is great, kudos to him! The book however seems to be a pretty half-assed work.

I was unsure if my difficulty reading it was mostly my own failure. But then I talked to some friends who also read some of it, and they agreed that the book is a very dry read.

Maybe I am trying to read the book in a suboptimal way (from cover to cover), but the effort needed to grasp each little subject is making me cringe.

I buy books about technologies because I want a better way to learn them than having to go through all the documentation. However, reading this book feels much harder than following through the online documentation of the tools it documents.

Now, the online docs for Pandas have syntax highlight, hyperlinking and even a 10 minute tutorial. Maybe it's more merit for the online docs than demerit for the book, I'm not sure.

It seems that the book content was written as a bunch of IPython notebooks, and then everything was later concatenated into a big document in an order that sort of made sense (and losing the good looks). Its examples are a bunch of throwaway code, and are not presented in a way that the reader can care about -- and believe me, I've been trying.

I guess when you are an expert in a subject, it can be hard to remember how it is to be a beginner again. However, that's precisely the exercise that would render a great book. I urge all tech authors to read and apply the principles explained in the Crash Course in Learning Theory. This would surely render better books for all of us.

The book is not all bad. I've certainly learned something from it. The effort to read all of it is just not worth it, though. For anyone who wants to learn more about NumPy, Scipy and Pandas, my recommendation is to use the online documentation.

Thank you Zé Ricardo for revising this text.

sábado, 5 de abril de 2014

Programando Erlang - 1 de Any

Na virada do ano, uma das minhas resoluções para 2014 foi a de que este ano aprenderia Erlang. Eu queria brincar com alguma linguagem que usasse ideias diferentes das que uso no trabalho e que me ensinasse mais do que simplesmente outro jeito de escrever os programas que já sei.

Como eu gostei de ler as ideias do Joe Armstrong no livro Coders at Work, e as descrições de Erlang na Internet me deixaram curioso, resolvi que em 2014 eu iria aprender Erlang. E assim, decidi por comprar logo o livro Programming Erlang, escrito pelo próprio Joe, e tentar aprender por ele.

Ainda não terminei de ler o livro, estou curtindo o aprendizado, acho que foi uma boa escolha de linguagem “extra” pra aprender. Mas como o livro fala sobre bastante coisa, resolvi escrever alguma coisa logo porque se deixar para escrever sobre o livro inteiro a tarefa vai ficar grande demais e eu vou fugir correndo com medo dela.

Então... Erlang!

É uma linguagem divertida. =)

Erlang é uma linguagem funcional com tipagem dinâmica, de propósito geral, focada em facilitar o desenvolvimento de programas concorrentes e que roda em uma máquina virtual própria (também chamada BEAM).

Mas Erlang é mais do que uma linguagem, podemos dizer que Erlang é todo um ambiente diferente. A VM de Erlang lembra um sistema operacional em muitas formas, possuindo seu próprio shell, seu próprio gerenciador de processos, seu esquema de atualização sem precisar parar nenhum processo e seus mecanismos disponíveis para comunicação entre processos.

Um processo Erlang não é nem um processo do sistema operacional nem uma thread: é um processo leve, muito mais leve que threads. A criação de um novo processo é praticamente gratuita -- pode-se dizer que criar um novo processo em Erlang é tão comum e tão sossegado quanto instanciar um novo objeto em Java.

Em Erlang, é idiomático encapsular funcionalidade em processos, e é comum um sistema ter milhares desses processos concorrentes, cada um com sua responsabilidade. Assim, esses processos encapsulando funcionalidades são análogos a serviços em uma arquitetura orientada a serviços, só que em Erlang eles aparecem numa forma bem natural na linguagem e mais integrada ao ambiente.

Aqui, permita-me apresentar algumas coisinhas da linguagem pra você, bem rapidão. Para acompanhar, instale Erlang (no Ubuntu: sudo apt-get install erlang).


Veja o seguinte esqueleto de um programa concorrente em Erlang:

Note a recursão na função loop: é assim que se faz processos iterativos (loops) em Erlang, que não tem sintaxe especial para isso. O compilador implementa a otimização de “recursão de cauda” (tail recursion) para fazer uma função escrita de maneira recursiva executar de forma iterativa  -- isto é, sem re-chamar a função aumentando a pilha de chamada (call stack).

Você pode testar esse código colocando-o em um arquivo esqueleto.erl e chamá-lo do shell Erlang, conforme a sessão abaixo (comandos digitados estão em negrito):

$ erl
Erlang R16B01 (erts-5.10.2) [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]

Eshell V5.10.2  (abort with ^G)
1> c(esqueleto).
2> Pid = esqueleto:start().
3> Pid ! "alo!".
Received: "alo!"
4> Pid ! 1234.
Received: 1234
5> Pid ! {teste, com, uma, tupla, 123, "ola"}.
Received: {teste,com,uma,tupla,123,"ola"}

Note os pontos finais no fim de cada comando: o comando não roda se você esquecer deles. Em Erlang, o ponto (.) separa comandos e declarações, o ponto-e-vírgula (;) separa cláusulas, e a vírgula (,) separa expressões.

A primeira linha, c(esqueleto). manda compilar o módulo no arquivo esqueleto.erl.

Na segunda linha, Pid = esqueleto:start(). aciona a função start() do módulo esqueleto, que vai gerar um novo processo e retornar o identificador do processo (em inglês, process identifier), que armazenamos na variável Pid.

A seguir, na terceira linha usamos o comando ! para envio de mensagens em Erlang, enviando a mensagem “alo!” para o processo criado na linha anterior, identificado por Pid. O comando receive da função loop() vai receber a mensagem e executar o código que imprime a mensagem na tela. A mensagem aparece repetida na tela porque o retorno do comando de envio (Pid ! Mensagem) é a mensagem enviada, e o shell sempre imprime o retorno do último comando executado.

As linhas seguintes apenas repetem o mesmo feito da linha anterior, com outras mensagens diferentes (um inteiro e uma tupla contendo átomos, inteiros e strings).

O comando receive tem algumas habilidades especiais: além de bloquear a execução do código até o processo receber uma mensagem (que também pode ter um timeout definido), ele pode selecionar o bloco de código a ser executado dependendo da forma ou conteúdo da mensagem, com o mecanismo chamado de pattern matching (ou, casamento de padrões).

Por exemplo, usando pattern matching, podemos alterar o comando receive da função loop() para executar um código diferente caso receba a mensagem “alo!”:

loop() ->
       "alo!" ->
           io:format("Alooooow, galerinha da paaishhh!"),
       Any ->
           io:format("Received: ~p~n", [Any]),

Se você repetir os passos anteriores com esse novo código, receberá uma resposta mais animada quando enviar a mensagem “alo!”.

Nesse exemplo, o padrão casado foi o conteúdo da mensagem, isto é, a string “alo!”. Mas o mecanismo permite fazer vários tipos de verificações: você pode, por exemplo, verificar se a mensagem é um número ou uma tupla, se é uma tupla contendo um determinado elemento, se é uma lista com tantos elementos, etc.

Quando alguém mais acostumado ao paradigma imperativo se depara com a ideia de
pattern matching (que existe em outras linguagens além de Erlang), usualmente acha útil pensar nele como um “switch-case em esteróides”, uma espécie de Super Estrutura Condicional.

Em Erlang, todavia, pattern matching (ou casamento de padrões) aparece em mais do que estruturas condicionais.

De fato, na expressão:

X = 1.

o = (igual) é um acionamento do operador de pattern matching de Erlang -- diferente de outras linguagens em que o igual é um operador de atribuição. Erlang não tem operador atribuição, que usualmente permite definir e redefinir o conteúdo de variáveis.

O que acontece aqui é que o operador de pattern matching liga um valor a uma variável quando esta ainda não tem nenhum valor associado (isto é, quando se trata de uma variável livre, ou unbound variable).

Na próxima vez que a variável for referenciada, ela terá o valor “casado” (matched) anteriormente, veja:

1> A.
* 1: variable 'A' is unbound
2> A="oi".
3> A.

Uma vez ligada, a variável só casará com o valor original, e nunca mais com outro. Caso tente casar a variável com outro valor (pensando que funcionaria como atribuição), você obterá um erro avisando que falhou o casamento do padrão:

4> A = "alô".
** exception error: no match of right hand side value "alô"

Isto porque em Erlang, as variáveis são imutáveis para o contexto local. Outra forma de dizer é: em Erlang, as variáveis são de atribuição única (single assignment variables). Uma vez atribuído um valor a uma variável, você não pode alterá-lo -- como demonstrei acima.

Isto parece estranho no começo, para quem está acostumado com o paradigma imperativo. Pensando bem, é a mesma estranheza que a gente sente quando aprende a programar e é exposto a variáveis e atribuição pela primeira vez (“como assim, x = x + 1?”). Faz mais sentido pensar nas variáveis de Erlang como as variáveis da Matemática, em que o valor de um nome é sempre o mesmo.

A imutabilidade de variáveis tem como consequência algumas coisas interessantes:

  • força você a criar novas variáveis em algumas situações, mesmo que esteja com pouca criatividade para dar um nome decente (no livro mesmo tem alguns exemplos com Word1, Word2 -- o que é meio feio)
  • evita alguns tipos de bugs e simplifica a depuração, pois aumenta a previsibilidade da execução do código (isto é, você pode confiar que o valor de uma variável não vai mudar)
  • facilita a escrita de programas concorrentes e permite que sejam rodados em paralelo, pois evita uma cacetada de problemas de memória compartilhada

E esta última consequência compensa as dificuldades de dar nomes às variáveis. Porque escrever programas multi-thread é difícil de fazer direito (você precisa se preocupar com sincronização de processos e de memória), e o jeito Erlang de escrever programas concorrentes que se comunicam via envio de mensagens (o modelo de atores) simplifica as coisas, de forma que os programas são sempre paralelizáveis.

Isso faz com que aplicações escritas em Erlang escalem mais fácil do que aplicações escritas em outras linguagens. Claro que vários problemas de escalabilidade se manterão, principalmente os relacionados a hardware e infraestrutura, mas problemas de software tendem a ser resolvidos mais fácil com Erlang.

De fato, Erlang vem de fábrica com mecanismos de clusterização, e você pode facilmente iniciar nós de um cluster em algumas máquinas e disparar chamadas remotas de um nó para outro.

Para um exemplo rápido, inicie um nó do cluster em um terminal, dando um nome:

$ erl -sname lennon@localhost
Erlang R16B01 (erts-5.10.2) [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]

Eshell V5.10.2  (abort with ^G)

Em outro terminal, inicie outro nó e faça uma chamada remota ao nó anterior:

$ erl -sname mccartney@localhost
Erlang R16B01 (erts-5.10.2) [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]

Eshell V5.10.2  (abort with ^G)
(mccartney@localhost)1> rpc:call(lennon@localhost, file, list_dir, ["/"]).

O argumento -sname faz com que Erlang inicie um nó com o nome passado (note como o nome do nó é mostrado no prompt). No comando que executamos no segundo nó (mccartney@localhost) usamos a função call do módulo rpc para acionar uma função remotamente em outro nó e obter o resultado -- neste caso, a função os:listdir(“/”) para retornar a lista de arquivos do diretório raiz.

E tudo com código da distribuição padrão. (Try that in Java, biátch!)

Bom, tentei mostrar algumas coisas interessantes da linguagem aqui, mas Erlang tem muito a oferecer ainda. Ela tem outros tipos de dados interessantes (átomos, binaries, tuplas, records, e na versão R17 terá maps), um mecanismo próprio de comunicação com outros processos do sistema operacional, permite a especificação opcional de tipos para funções, um banco de dados para aplicações distribuídas, vem com ferramentas de fábrica para análise estática de código, depuração e profiling e com um conjunto de bibliotecas e funções para aplicações tolerante a falhas e distribuídas.

Só tem dois problemas de usabilidade que me incomodam um pouco:

O resto é fantástico. =)

Caso você tenha ficado curioso, pode querer conferir o livro Learn You Some Erlang -- não li, mas já tirei algumas dúvidas nele caindo de buscas do Google e pareceu muito bom. Have a nice journey! :)

Valeu pela revisão, Fredi e Denise.