Não faz muito tempo, escrevi sobre o uso do API de subdocumento do Couchbase Server com o Go SDK. Realizar operações de subdocumento é incrivelmente útil se você quiser alterar ou acessar uma parte de um documento NoSQL potencialmente enorme. As operações de subdocumento economizam recursos de rede e são ótimas para o desempenho.
Um colega meu perguntou como fazer mutações de subdocumentos em massa por chave, de forma semelhante à que demonstrei em meu tutorial intitulado, Uso da Golang para obter vários documentos do Couchbase por chave em uma única operação. A resposta curta é que não é possível com uma única operação, mas como a Golang é incrivelmente rápida e incrível, você pode fazer as coisas em paralelo e obter os mesmos resultados.
Veremos como realizar mutações de subdocumentos de forma assíncrona com base em uma lista de IDs de documentos com a linguagem de programação Go e o Couchbase.
Vamos imaginar um cenário do mundo real que queremos realizar. Digamos que temos um site de rede profissional que recebe dezenas de milhões de solicitações por segundo. Como o desempenho é essencial, decidimos agrupar algumas dessas solicitações da melhor forma possível. Digamos que 100 pessoas acabaram de atualizar seus perfis para incluir Golang como uma de suas habilidades. Queremos acrescentar isso em sua lista de habilidades.
Daqui para frente, faremos tudo em um main.go em algum lugar em nosso arquivo $GOPATH caminho. Abra esse arquivo e inclua o seguinte:
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 |
pacote principal importação ( "fmt" "sync" "github.com/couchbase/gocb" ) var waitGroup sincronização.Grupo de espera var dados chan string var balde *gocb.Balde func trabalhador() {} func principal() { fmt.Println("Iniciando o aplicativo...") documentIds := []string{"nraboy", "jmichaels", "tgreenstein"} agrupamento, _ := gocb.Conectar("couchbase://localhost") balde, _ = agrupamento.OpenBucket("default", "") fmt.Println("O aplicativo foi concluído!") } |
Vamos analisar o que temos até o momento.
Como planejamos fazer coisas em paralelo com goroutines, precisamos saber quando é seguro encerrar nosso aplicativo. O Grupo de espera
nos permite acompanhar nossas tarefas assíncronas e esperar até que todas tenham terminado antes de continuar. Como estaremos processando dados com goroutines, precisaremos de um canal que todas as goroutines usem ao mesmo tempo. Cada uma de nossas goroutines será uma instância do canal trabalhador
método.
No principal
estamos declarando todas as chaves que receberão a mutação. Em um cenário de produção, a lógica comercial do aplicativo provavelmente agregará essa lista de chaves.
Também estamos estabelecendo uma conexão com o Couchbase.
Com a base estabelecida, vamos dar uma olhada no principal
método.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
func principal() { fmt.Println("Iniciando o aplicativo...") documentIds := []string{"nraboy", "jmichaels", "tgreenstein"} dados = fazer(chan string) agrupamento, _ := gocb.Conectar("couchbase://localhost") balde, _ = agrupamento.OpenBucket("default", "") para i := 0; i < 2; i++ { waitGroup.Adicionar(1) ir trabalhador() } para i := 0; i < len(documentIds); i++ { dados <- documentIds[i] } próximo(dados) waitGroup.Aguarde() fmt.Println("O aplicativo foi concluído!") } |
Em Go, podemos ativar uma quantidade ridiculamente grande de goroutines que serão executadas em paralelo. É claro que o número real que você pode ativar depende do seu hardware, mas, por enquanto, vamos ser conservadores com dois. Para cada trabalhador
que iniciamos, aumentamos o Grupo de espera
. Quando essas goroutines param, o Grupo de espera
diminuirá, o que acabará por desbloquear o aplicativo e permitir que ele seja encerrado.
Você também notará que adicionamos um canal para nossos dados de cadeia de caracteres. Cada um de nossos IDs de documento desejados é adicionado ao canal e, em seguida, o canal é fechado. Você verá por que fazemos isso quando definirmos a função trabalhador
lógica.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
func trabalhador() { adiar waitGroup.Feito() para { id, ok := <-dados se !ok { quebra } _, erro := balde.Mutação(id, 0, 0).ArrayAppend("habilidades", "Golang", verdadeiro).Executar() se erro != nulo { fmt.Printf("%s - %v\n", id, erro) } } } |
O trecho acima é o nosso trabalhador
lógica do método. Quando a função termina, o adiar
é executado, o que subtrai do Grupo de espera
.
Cada trabalhador
será executado para sempre por meio de um loop. Cada iteração do loop pegará os ids do dados
canal. Se não estivermos ok
isso provavelmente significa que o canal está vazio e que devemos encerrar o loop. Se obtivermos um ID, planeje fazer uma mutação nesse documento e anexar uma nova cadeia de caracteres no habilidades
que presumimos ser uma matriz. Se a matriz não existir no documento, será criada uma.
Se houver um erro por qualquer motivo, talvez a chave não exista, imprima que ocorreu um erro.
O código completo dessa demonstração simples é o seguinte:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
pacote principal importação ( "fmt" "sync" "github.com/couchbase/gocb" ) var waitGroup sincronização.Grupo de espera var dados chan string var balde *gocb.Balde func trabalhador() { adiar waitGroup.Feito() para { id, ok := <-dados se !ok { quebra } _, erro := balde.Mutação(id, 0, 0).ArrayAppend("habilidades", "Golang", verdadeiro).Executar() se erro != nulo { fmt.Printf("%s - %v\n", id, erro) } } } func principal() { fmt.Println("Iniciando o aplicativo...") documentIds := []string{"nraboy", "jmichaels", "tgreenstein"} dados = fazer(chan string) agrupamento, _ := gocb.Conectar("couchbase://localhost") balde, _ = agrupamento.OpenBucket("default", "") para i := 0; i < 2; i++ { waitGroup.Adicionar(1) ir trabalhador() } para i := 0; i < len(documentIds); i++ { dados <- documentIds[i] } próximo(dados) waitGroup.Aguarde() fmt.Println("O aplicativo foi concluído!") } |
Novamente, essas mutações de subdocumentos para o habilidades
array acontecem em paralelo por meio de goroutines. Para obter mais informações sobre o uso de goroutines para fazer coisas ao mesmo tempo, consulte um tutorial anterior que escrevi sobre o assunto, intitulado, Aplicativos Golang simultâneos com goroutines e canais.
Conclusão
Você acabou de ver outra demonstração de como fazer mutações de subdocumentos com o Couchbase e o Golang. Desta vez, exploramos a possibilidade de fazer as coisas em paralelo em vez de tentar usar um dos operadores em massa. Ao fazer as coisas em paralelo, obtemos quase o mesmo desempenho de operações em massa em uma lista de chaves.
Oi Nic, ótimo artigo. Meu caso de uso é executar
volume
operações de crude em cerca de uma centena desubdocumentos
e, em seguida, uma lista de chaves de documentos. Como você disse e concluiu no artigoUm colega meu perguntou como fazer mutações de subdocumentos em massa por chave, semelhante ao que demonstrei em meu tutorial intitulado Using Golang to get Multiple Couchbase Documents by Key in a Single Operation (Usando Golang para obter vários documentos do Couchbase por chave em uma única operação). A resposta curta é que não é possível fazer isso com uma única operação, mas, como a Golang é incrivelmente rápida e incrível, você poderia fazer as coisas em paralelo e obter os mesmos resultados.
Desta vez, exploramos a possibilidade de fazer as coisas em paralelo em vez de tentar usar um dos operadores em massa. Ao fazer as coisas em paralelo, obtemos quase o mesmo desempenho que ao fazer operações em massa em uma lista de chaves.
Gostaria de confirmar que, como estaremos realizando centenas de chamadas de rede de qualquer maneira, como isso é eficiente?