No hace mucho escribí sobre el uso de la función API de subdocumentos de Couchbase Server con el SDK de Go. Hacer operaciones con subdocumentos es increíblemente útil si quieres cambiar o acceder a una parte de un documento NoSQL potencialmente enorme. Las operaciones con subdocumentos ahorran recursos de red y mejoran el rendimiento.
Un colega me ha preguntado cómo se podrían hacer mutaciones masivas de subdocumentos por clave, de forma similar a como lo demostré en mi tutorial titulado, Uso de Golang para obtener múltiples documentos Couchbase por clave en una única operación. La respuesta corta es que no se puede con una sola operación, pero como Golang es tan increíblemente rápido e impresionante, se podrían hacer cosas en paralelo y obtener los mismos resultados.
Vamos a ver cómo realizar de forma asíncrona mutaciones de subdocumentos basadas en una lista de ids de documentos con el lenguaje de programación Go y Couchbase.
Imaginemos un escenario real que queremos lograr. Digamos que tenemos un sitio web de redes profesionales que recibe decenas de millones de peticiones por segundo. El rendimiento es una necesidad, así que decidimos que queremos agrupar algunas de esas peticiones lo mejor que podamos. Digamos que 100 personas acaban de actualizar su perfil para incluir Golang como una de sus habilidades. Queremos añadirlo a su lista de habilidades.
En el futuro, vamos a hacer todo en un main.go en algún lugar de nuestro $GOPATH ruta. Abra este archivo e incluya lo siguiente:
|
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 |
paquete principal importar ( "fmt" "sync" "github.com/couchbase/gocb" ) var waitGroup sincronizar.Grupo de espera var datos chan cadena var cubo *gocb.Cubo func trabajador() {} func principal() { fmt.Imprimir("Iniciando la aplicación...") documentIds := []cadena{"nraboy", "jmichaels", "tgreenstein"} grupo, _ := gocb.Conectar("couchbase://localhost") cubo, _ = grupo.OpenBucket("por defecto", "") fmt.Imprimir("¡La solicitud se ha completado!") } |
Desglosemos lo que tenemos hasta ahora.
Como planeamos hacer cosas en paralelo con goroutines, necesitamos saber cuándo es seguro terminar nuestra aplicación. El Grupo de espera nos permite hacer un seguimiento de nuestras tareas asíncronas y esperar hasta que todas hayan terminado antes de continuar. Dado que vamos a procesar datos con goroutines, necesitaremos un canal que todas las goroutines puedan utilizar al mismo tiempo. Cada una de nuestras goroutines será una instancia del canal trabajador método.
En el principal estamos declarando todas las claves que recibirán la mutación. En un escenario de producción, la lógica de negocio de la aplicación probablemente agregará esta lista de claves.
También estamos estableciendo una conexión con Couchbase.
Una vez sentadas las bases, analicemos con más detalle la 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.Imprimir("Iniciando la aplicación...") documentIds := []cadena{"nraboy", "jmichaels", "tgreenstein"} datos = escriba a(chan cadena) grupo, _ := gocb.Conectar("couchbase://localhost") cubo, _ = grupo.OpenBucket("por defecto", "") para i := 0; i < 2; i++ { waitGroup.Añadir(1) ir trabajador() } para i := 0; i < len(documentIds); i++ { datos <- documentIds[i] } cerrar(datos) waitGroup.Espere() fmt.Imprimir("¡La solicitud se ha completado!") } |
En Go, podemos hacer girar una cantidad ridículamente grande de goroutines que se ejecutarán en paralelo. Por supuesto, el número real que puede girar depende de su hardware, pero por ahora, vamos a ser conservadores con dos. Por cada trabajador que empezamos, aumentamos el Grupo de espera. Cuando estas goroutines se detienen, el Grupo de espera disminuirá, lo que finalmente desbloqueará la aplicación y permitirá que termine.
También observará que hemos añadido un canal para nuestros datos de cadena. Cada uno de nuestros ids de documento deseados se añaden al canal y luego se cierra el canal. Verás por qué hacemos esto cuando definamos la función trabajador lógica.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
func trabajador() { aplazar waitGroup.Hecho() para { id, ok := <-datos si !ok { romper } _, err := cubo.MutateIn(id, 0, 0).ArrayAppend("habilidades", "Golang", verdadero).Ejecute() si err != nil { fmt.Imprimir("%s - %v\n", id, err) } } } |
El fragmento anterior es nuestro trabajador lógica del método. Cuando la función termina, el aplazar que sustrae del Grupo de espera.
Cada trabajador se ejecutará eternamente a través de un bucle. Cada iteración del bucle tomará ids de la base de datos datos canal. Si no estamos okprobablemente significa que el canal está vacío y debemos terminar el bucle. Si obtenemos un id, planea hacer una mutación en ese documento y añadir una nueva cadena en el archivo competencias que asumimos que es un array. Si el array no existe en el documento, se creará uno.
Si se produce un error por cualquier motivo, tal vez la clave no existe, imprima que se ha producido un error.
El código completo de esta sencilla demostración es el siguiente:
|
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 |
paquete principal importar ( "fmt" "sync" "github.com/couchbase/gocb" ) var waitGroup sincronizar.Grupo de espera var datos chan cadena var cubo *gocb.Cubo func trabajador() { aplazar waitGroup.Hecho() para { id, ok := <-datos si !ok { romper } _, err := cubo.MutateIn(id, 0, 0).ArrayAppend("habilidades", "Golang", verdadero).Ejecute() si err != nil { fmt.Imprimir("%s - %v\n", id, err) } } } func principal() { fmt.Imprimir("Iniciando la aplicación...") documentIds := []cadena{"nraboy", "jmichaels", "tgreenstein"} datos = escriba a(chan cadena) grupo, _ := gocb.Conectar("couchbase://localhost") cubo, _ = grupo.OpenBucket("por defecto", "") para i := 0; i < 2; i++ { waitGroup.Añadir(1) ir trabajador() } para i := 0; i < len(documentIds); i++ { datos <- documentIds[i] } cerrar(datos) waitGroup.Espere() fmt.Imprimir("¡La solicitud se ha completado!") } |
De nuevo, estas mutaciones de subdocumentos a la competencias array ocurren en paralelo a través de goroutines. Para obtener más información sobre el uso de goroutines para hacer las cosas al mismo tiempo, echa un vistazo a un tutorial anterior que escribí sobre el tema titulado, Aplicaciones Golang concurrentes con Goroutines y Canales.
Conclusión
Acabas de ver otra demo para hacer mutaciones de subdocumentos con Couchbase y Golang. Esta vez exploramos hacer las cosas en paralelo en lugar de tratar de utilizar uno de los operadores a granel. Haciendo las cosas en paralelo conseguimos casi el mismo rendimiento que haciendo operaciones masivas sobre una lista de claves.
Hola Nic, muy buen artículo. Mi caso de uso es realizar
a graneloperaciones en un centenar desubdocumentos, dada una lista de claves de documentos. Como usted dijo y concluyó en el artículoUn colega me ha preguntado cómo se podrían hacer mutaciones masivas de subdocumentos por clave, de forma similar a como lo demostré en mi tutorial titulado, Using Golang to get Multiple Couchbase Documents by Key in a Single Operation. La respuesta corta es que no se puede con una sola operación, pero como Golang es tan increíblemente rápido y asombroso, se podrían hacer cosas en paralelo y obtener los mismos resultados.Esta vez exploramos la posibilidad de hacer las cosas en paralelo en lugar de intentar utilizar uno de los operadores masivos. Haciendo las cosas en paralelo obtenemos casi el mismo rendimiento que haciendo operaciones masivas sobre una lista de claves.Quiero confirmar que, dado que de todos modos realizaremos cientos de llamadas a la red, ¿cómo es esto eficiente entonces?