O próximo libcouchbase A versão do Windows® contará com atualizações de cluster aprimoradas e uma série de outras melhorias de estabilidade e desempenho. A maior parte do novo trabalho gira em torno do Publicação da operadora de configuração de cluster ou CCCP para abreviar.
O Couchbase é um escalável, elástico cluster. Parte desse conjunto de recursos significa que os nós do cluster podem ser adicionados ou removidos livremente sem causar tempo de inatividade no nível do aplicativo. A troca de todos os nós de um cluster e sua substituição é totalmente suportada e algo normalmente transparente para um servidor de aplicativos usando o SDK.
O próprio SDK deve garantir que está se comunicando com um cluster íntegro e que está ciente dos vários nós que são membros do cluster. Especificamente, isso inclui saber duas coisas:
- Quais nós fazem parte do cluster
- Qual nó é responsável por uma determinada chave
Essas informações são transferidas por meio de um objeto JSON chamado "Mapa de configuração" ou "Mapa de clusters". Você mesmo pode ver isso navegando para um URL como
Esse objeto JSON contém as listas dos nós, bem como um grande "Mapa do vBucket", que basicamente instrui o cliente sobre qual nó é responsável por qual vBucket, e o cliente então faz o hash das chaves nesse mapa.
Bootstrapping
Para ver como o SDK (especificamente, libcouchbase) juntando tudo isso, podemos examinar o estirpe saída de cbc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 6 connect(6, {sa_family=AF_INET, sin_port=htons(8091), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operação em andamento) connect(6, {sa_family=AF_INET, sin_port=htons(8091), sin_addr=inet_addr("127.0.0.1")}, 16) = 0 sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"GET /pools/default/bucketsStream"..., 145}], msg_controllen=0, msg_flags=0}, 0) = 145 recvmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"HTTP/1.1 200 OKrnTransfer-Encodi"..., 65536}], msg_controllen=0, msg_flags=0}, 0) = 200 recvmsg(6, 0x7fff7b7e9bb0, 0) = -1 EAGAIN (Recurso temporariamente indisponível) recvmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"2223rn{"name": "protected", "bucke"..., 65536}], msg_controllen=0, msg_flags=0}, 0) = 8756 recvmsg(6, 0x7fff7b7e9bb0, 0) = -1 EAGAIN (Recurso temporariamente indisponível) socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 7 connect(7, {sa_family=AF_INET, sin_port=htons(11210), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operação em andamento) connect(7, {sa_family=AF_INET, sin_port=htons(11210), sin_addr=inet_addr("127.0.0.1")}, 16) = 0 getsockname(7, {sa_family=AF_INET, sin_port=htons(34432), sin_addr=inet_addr("127.0.0.1")}, [16]) = 0 getpeername(7, {sa_family=AF_INET, sin_port=htons(11210), sin_addr=inet_addr("127.0.0.1")}, [16]) = 0 sendmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"200 ", 24}], msg_controllen=0, msg_flags=0}, 0) = 24 recvmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"201 16CRAM-MD5"..., 65536}], msg_controllen=0, msg_flags=0}, 0) = 38 recvmsg(7, 0x7fff7b7e9b70, 0) = -1 EAGAIN (Recurso temporariamente indisponível) sendmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"200!1010CRAM-MD5", 32}], msg_controllen=0, msg_flags=0}, 0) = 32 recvmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"201!!36<9003993"..., 65536}], msg_controllen=0, msg_flags=0}, 0) = 54 recvmsg(7, 0x7fff7b7e9b70, 0) = -1 EAGAIN (Recurso temporariamente indisponível) sendmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"200"10002CRAM-MD5"..., 74}], msg_controllen=0, msg_flags=0}, 0) = 74 recvmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"201 "rAuthenti"..., 65536}], msg_controllen=0, msg_flags=0}, 0) = 37 recvmsg(7, 0x7fff7b7e9b70, 0) = -1 EAGAIN (Recurso temporariamente indisponível) sendmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"2003s31foo", 27}], msg_controllen=0, msg_flags=0}, 0) = 27 recvmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"20142111~$@327-17Hell"..., 65536}], msg_controllen=0, msg_flags=0}, 0) = 41 recvmsg(7, 0x7fff7b7e9b70, 0) = -1 EAGAIN (Recurso temporariamente indisponível) "foo" Tamanho:13 Flags:0 CAS:f2dd740247e0100 Olá mundo! |
Acima, você pode ver que a biblioteca primeiro faz uma solicitação HTTP para a porta 8091 para recuperar o mapa do cluster. Depois que o mapa do cluster é recuperado, ele se conecta ao nó que é o Mestre do vBucket para a chave "foo" na porta 11210, realiza a autenticação SASL e, por fim, emite um memcached solicitação da chave.
Com o CCCP, a porta do memcached em 11210 também pode hospedar as informações de configuração. Portanto, se olharmos para um rastreamento semelhante do próximo DP versão do cbc ferramenta, veremos isso:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
connect(6, {sa_family=AF_INET, sin_port=htons(11210), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operação em andamento) connect(6, {sa_family=AF_INET, sin_port=htons(11210), sin_addr=inet_addr("127.0.0.1")}, 16) = 0 getsockname(6, {sa_family=AF_INET, sin_port=htons(34412), sin_addr=inet_addr("127.0.0.1")}, [16]) = 0 getpeername(6, {sa_family=AF_INET, sin_port=htons(11210), sin_addr=inet_addr("127.0.0.1")}, [16]) = 0 sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"200 ", 24}], msg_controllen=0, msg_flags=0}, 0) = 24 recvmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"201 16CRAM-MD5"..., 65536}], msg_controllen=0, msg_flags=0}, 0) = 38 recvmsg(6, 0x7fff8206e770, 0) = -1 EAGAIN (Recurso temporariamente indisponível) sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"200!1010CRAM-MD5", 32}], msg_controllen=0, msg_flags=0}, 0) = 32 recvmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"201!!36<9014490"..., 65536}], msg_controllen=0, msg_flags=0}, 0) = 54 recvmsg(6, 0x7fff8206e770, 0) = -1 EAGAIN (Recurso temporariamente indisponível) sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"200"10002CRAM-MD5"..., 74}], msg_controllen=0, msg_flags=0}, 0) = 74 recvmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"201 "rAuthenti"..., 65536}], msg_controllen=0, msg_flags=0}, 0) = 37 recvmsg(6, 0x7fff8206e770, 0) = -1 EAGAIN (Recurso temporariamente indisponível) sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"200265r360", 24}], msg_controllen=0, msg_flags=0}, 0) = 24 recvmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"20126536{r360{"rev":1"..., 65536}], msg_controllen=0, msg_flags=0}, 0) = 7827 recvmsg(6, 0x7fff8206e770, 0) = -1 EAGAIN (Recurso temporariamente indisponível) sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"2003s31foo", 27}], msg_controllen=0, msg_flags=0}, 0) = 27 recvmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"20142111~$@327-17Hell"..., 65536}], msg_controllen=0, msg_flags=0}, 0) = 41 recvmsg(6, 0x7fff8206e730, 0) = -1 EAGAIN (Recurso temporariamente indisponível) "foo" Tamanho:13 Flags:0 CAS:f2dd740247e0100 Olá mundo! |
Aqui, reduzimos uma conexão TCP extra ao buscar a configuração diretamente da porta de dados.
Mesmo em um cluster de host local, o segundo exemplo é executado duas vezes mais rápido que o primeiro, devido à redução da conexão HTTP inicial.
Atualizações de configuração
As atualizações de configuração são mapas de cluster novos e atualizados recebidos quando a topologia do cluster é alterada. Isso permite que um cliente saiba que os nós foram adicionados ou removidos e, posteriormente, que o mapa do vBucket foi alterado.
O comportamento anterior da libcouchbase era conectar-se ao transmissão para o bucket fornecido a fim de receber atualizações de configuração dele. Isso exigia que a biblioteca mantivesse uma conexão TCP quase sempre ociosa, que receberia informações de configuração do cluster. Essa abordagem apresentava duas desvantagens principais:
- Isso colocaria a biblioteca em um estado de espera/longa sondagem, em que o soquete que estava servindo a configuração assumiria que estava conectado a um nó em funcionamento que empurrar ele informações de configuração. Essa suposição falharia, é claro, se o nó ao qual o cliente estivesse conectado por meio do endpoint de streaming fosse aquele que falhasse (e pior, nunca entregasse um TCP RST). Como a semântica era baseada em push, o cliente não poderia definir um tempo limite para garantir que a conexão ainda estivesse funcionando corretamente.
- Uma conexão TCP adicional era necessária o tempo todo para manter as informações de configuração atualizadas. Como os pontos de extremidade da API REST foram projetados para administração, e não para uso rotineiro, eles não são otimizados para uso de memória. Especificamente, cada conexão TCP com eles incorre em uma penalidade significativa de recursos no lado do servidor.
Confmon - Gerenciador de configuração
A nova versão 2.3 altera o modelo e a abordagem de como a configuração é recuperada. Um conjunto interno de APIs conhecido coletivamente como confmon/clconfig foram introduzidos na base de código. Em vez de ter um modelo intrusivo baseado em push, em que a configuração seria imposta em um soquete aberto, confmon é baseado em pull e é acionado somente conforme a necessidade. Assim, o cliente, por padrão, não manterá soquetes abertos apenas para configuração; em vez disso, ele assumirá uma configuração válida até atingir um determinado limite de erro ou receber uma mensagem explícita de NÃO MEU_VBUCKET de um dos nós do cluster (indicando que o mapa do cliente está sem dados).
Especificamente com o novo CCCP aprimoramentos, cada NÃO MEU_VBUCKET resposta em si mesma já contém o mapa de cluster atualizado, eliminando assim a necessidade de buscar novamente a configuração.
Os benefícios também são obtidos com clusters mais antigos, pois o novo modelo abre apenas conexões de API REST sob demanda - em vez de mantê-las abertas indefinidamente. (Na verdade, fizemos uma pequena otimização em que fazemos uma sondagem longa por um curto período de tempo, pois as atualizações de configuração tendem a ocorrer em sucessão durante as alterações de topologia, como rebalanceamentos).
Registro em log
Ganchos de registro foram adicionados à biblioteca. Esse modelo permite que você ative o registro padrão registrador de console definindo o LCB_LOGLEVEL ou instale seus próprios ganchos de registro implementando a variável de ambiente lcb_logprocs e informando a instância sobre seus ganchos de registro.
O registro em log foi adicionado para eventos notáveis, mas não intensivos em CPU, como tempos limite, conexões de soquete, destruição de soquete, atualizações de configuração e muito mais.
Observe que há mais coisas que gostaríamos de registrar e este não é o fim de toda a instrumentação e dos auxílios de diagnóstico que planejamos adicionar!
Gerenciamento de conexões
Também aprimoramos a maneira como lidamos com novas conexões aos nós de "dados" do memcached. Anteriormente, as conexões tinham como escopo os objetos que as criaram. Isso significava que o lcb_server_t abriria e fecharia uma conexão.
Na versão 2.3, adicionamos um connmgr que funciona como um pool de soquetes (e será usado no futuro para dar suporte ao pool de soquetes para coisas como consultas de visualização). Em vez de ter subsistemas aberto e próximo conexões do sistema de E/S diretamente, eles agora solicitação e liberação (ou descartar) conexões de e para o connmgr instância
Agora, as estruturas do servidor não fecharão incondicionalmente suas conexões TCP, mas verificarão se há dados pendentes nelas; se houver dados, o soquete será descartado de volta ao pool (ou seja, o soquete é liberado e todos os recursos do pool associados a ele também são liberados) porque consideramos que o soquete está em um estado inválido (já que as respostas adicionais do servidor provavelmente serão NÃO MEU_VBUCKET respostas). Se não houver dados pendentes, o soquete será liberado de volta ao pool, tornando-se disponível para uma solicitação subsequente de uma nova conexão. Em nossos testes, isso mostrou uma redução de até 6 vezes na criação de novas conexões TCP durante as alterações na topologia do cluster.
Obter o código
Você pode fazer download de um tarball de origem dessa versão em
http://packages.couchbase.com/clients/c/snapshots/libcouchbase-2.3.0_dp2.tar.gz
Existe alguma referência sobre como criar drivers personalizados para o couchbase? Ainda não há nenhum driver Erlang nativo para o couchbase e gostaria de saber se há algum documento coerente que possa ajudar a desenvolvê-lo.