O Couchbase Server 7.0 agora oferece suporte a transações N1QL.
Vamos tentar escrever um programa em C que execute um conjunto de transações em um único nó.
Etapa 1: Primeiro decidimos como chamar o programa. As entradas serão o URL do bucket do Couchbase no qual queremos executar as consultas e as credenciais (nome de usuário seguido da senha).
Uso:|
1 |
./n1ql couchbase://localhost/test Administrator password |
Nas transações N1QL, um valor txid é retornado pelo comando START TRANSACTION. Esse valor é usado em todas as consultas N1QL subsequentes dentro da transação até o COMMIT ou ROLLBACK final. Portanto, devemos declarar um ID de transação na função principal, que pode ser passado para as solicitações de consulta restantes que fazem parte da transação.
Etapa 2: Inicializa um ponteiro que será usado para salvar o ID da transação da consulta BEGIN TRANSACTION. Esse ID será usado em toda a transação.
|
1 |
char *transaction_id = (char *)malloc(64 * sizeof(char)); |
Etapa 3: Inicializar o cluster
Uma conexão com um cluster do Couchbase Server é representada por um lcb_INSTANCE objeto.
lcb_INSTANCE *instance;
O conjunto de operações permitidas depende do tipo desse objeto e se o bucket está associado a ele. Aqui, usamos o tipo Cluster. A maneira mais simples de criar um objeto de cluster é chamar lcb_create para criar um identificador do Couchbase, passando LCB_TYPE_CLUSTER com uma string de conexão, nome de usuário e senha. Em seguida, programamos uma conexão usando lcb_connect() e verificamos se o bucket existe.
|
1 2 3 4 5 6 7 8 9 |
lcb_CREATEOPTS *create_options = NULL; lcb_createopts_create(&create_options, LCB_TYPE_CLUSTER); lcb_createopts_connstr(create_options, argv[1], strlen(argv[1])); lcb_createopts_credentials(create_options, argv[2], strlen(argv[2]), argv[3], strlen(argv[3])); check(lcb_create(&instance, create_options), "create couchbase handle"); lcb_createopts_destroy(create_options); check(lcb_connect(instance), "schedule connection"); lcb_wait(instance, LCB_WAIT_DEFAULT); check(lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_BUCKETNAME, &bucket), "get bucket name"); |
Etapa 4: Execute as consultas em query.h
Temos as consultas correspondentes à nossa transação definidas em queries.h.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
BEGIN WORK INSERT INTO test VALUES(\"kkk1\", {\"a\":1}) SELECT d.*, META(d).id FROM test AS d WHERE d.a >= 0 SAVEPOINT s1 UPDATE test AS d SET d.b = 10 WHERE d.a > 0 SELECT d.*, META(d).id FROM test AS d WHERE d.a >= 0 SAVEPOINT s2 UPDATE test AS d SET d.b = 10, d.c = \"xyz\" WHERE d.a > 0 SELECT d.*, META(d).id FROM test AS d WHERE d.a >= 0 ROLLBACK TRAN TO SAVEPOINT s2 SELECT d.*, META(d).id FROM test AS d WHERE d.a >= 0 INSERT INTO test VALUES(\"kkk2\", {\"a\":2}) UPDATE test AS d SET d.b = 20, d.c = \"xyz\" WHERE d.a > 0 COMMIT WORK |
Precisamos usar uma biblioteca de análise de JSON em C para tratar os resultados da consulta e extrair o ID da transação. Aqui podemos usar a biblioteca json-c. Para obter o ID da transação da instrução BEGIN WORK (a primeira instrução), usamos a função de retorno de chamada txid. Para processar e executar as outras consultas, chamamos a função de retorno de chamada de linha. Isso retornará as linhas de resultado.
|
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 |
for> (ii = >0>; ii < num_queries; ii++) {> >lcb_CMDQUERY> *>cmd>; > >lcb_cmdquery_create>(&cmd);> >check>(lcb_cmdquery_statement(cmd, queries[ii].query, strlen(queries[ii].query)), >"set QUERY statement">);> >printf>(>"----> \x1b[1m%s\x1b[0m\n">, queries[ii].query);> >if> (ii == >0>) {> >lcb_cmdquery_callback>(cmd, txid_callback);> >lcb_wait>(instance, LCB_WAIT_DEFAULT);> >check>(lcb_query(instance, transaction_id, cmd), >"schedule QUERY operation">);> >lcb_wait>(instance, LCB_WAIT_DEFAULT); > } >else> {> >char> buf>[100];> >sprintf>(buf,>"\">%s\>"">,transaction_id);> >lcb_cmdquery_callback>(cmd, row_callback);> >// SET rest option pretty to true and txtimeout to 3s> >check>(lcb_cmdquery_option(cmd, >"pretty">, strlen(>"pretty">), >"true">, strlen(>"true">)),>"set QUERY 'pretty' option">);> >check>(lcb_cmdquery_option(cmd, >"txtimeout">, strlen(>"txtimeout">), >"\">3s>\>"">, strlen(>"\">3s>\>"">)),>"set QUERY 'txtimeout' option">);> check(lcb_cmdquery_option(cmd, >"txid">, >strlen>(>"txid">),buf, >strlen>(buf)),>"set QUERY 'txtimeout' option">);> check(lcb_query(instance, >NULL>, cmd), >"schedule QUERY operation">);> lcb_wait(instance, LCB_WAIT_DEFAULT);> } > lcb_cmdquery_destroy(cmd);> lcb_wait(instance, LCB_WAIT_DEFAULT);> }> |
Antes de executar as consultas, definimos três parâmetros de consulta: pretty, txtimeout (tempo limite da transação) e o txid que obtivemos na primeira instrução. Esses são parâmetros em nível de solicitação.
Agora vamos nos aprofundar nas funções de retorno de chamada
Txid_Callback -
Aqui precisamos analisar a resposta JSON da execução da instrução BEGIN WORK para extrair o txid a ser passado para as instruções subsequentes como um parâmetro de consulta usando a biblioteca JSONC. Para isso, usamos o ponteiro criado anteriormente e o definimos no método lcb_respquery_cookie. Isso define o cookie da operação, o que significa que quando executamos lcb_query, há um argumento de cookie para ele e lcb_respquery_cookie obtém esse ponteiro em nossa função de retorno de chamada. (Criamos um ponteiro na função principal e o definimos na função de retorno de chamada).
|
1 2 3 4 5 6 7 8 9 10 |
/* create pointer transaction_id and set it in the callback */ lcb_respquery_cookie(resp, (void **)&transaction_id); check(lcb_respquery_status(resp),"check response status"); lcb_respquery_row(resp, &row, &nrow); if (!lcb_respquery_is_final(resp)) { parsed_json = json_tokener_parse(row); json_object_object_get_ex(parsed_json, "txid", &txid_obj); temp = json_object_get_string(txid_obj); strcpy(transaction_id,temp); } |
Callback de linha - É usado para analisar e recuperar as linhas de resultado da execução da consulta.
|
1 2 3 4 5 6 7 8 9 |
lcb_STATUS rc = lcb_respquery_status(resp); lcb_respquery_row(resp, &row, &nrow); ln2space(row, nrow); fprintf(stderr, "[\x1b[%dmQUERY\x1b[0m] %s, (%d) %.*s\n", err2color(rc), lcb_strerror_short(rc), (int)nrow, (int)nrow, row); if (lcb_respquery_is_final(resp)) { fprintf(stderr, "\n"); } } |
Aqui, obtemos a resposta da consulta, obtemos as linhas e as imprimimos.
Usando o exemplo acima, agora podemos usar as transações N1QL no C SDK. Para obter o código completo e instruções sobre como executá-lo, consulte - https://github.com/ikandaswamy/CBSDK_N1QLExamples