1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2013-2017 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 /*
19  * BUILD: `cc -o observe observe.c -lcouchbase`
20  * RUN: `./observe key`
21  */
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <assert.h>
26 #include <libcouchbase/couchbase.h>
27 #include <libcouchbase/api3.h>
28 
29 #define fail(msg) \
30     fprintf(stderr, "%s\n", msg); \
31     exit(EXIT_FAILURE)
32 
33 #define fail2(msg, err) \
34     fprintf(stderr, "%s\n", msg); \
35     fprintf(stderr, "Error was 0x%x (%s)\n", err, lcb_strerror(NULL, err)); \
36     exit(EXIT_FAILURE)
37 
38 typedef struct {
39     int master;
40     lcb_U8 status;
41     lcb_U64 cas;
42 } node_info;
43 
44 typedef struct {
45     unsigned nresp;
46     node_info *nodeinfo;
47 } observe_info;
48 
49 static void
observe_callback(lcb_t instance,int cbtype,const lcb_RESPBASE * rb)50 observe_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb)
51 {
52     const lcb_RESPOBSERVE *resp = (const lcb_RESPOBSERVE*)rb;
53     observe_info *obs_info = (observe_info *)rb->cookie;
54     node_info *ni = &obs_info->nodeinfo[obs_info->nresp];
55 
56     if (rb->nkey == 0) {
57         fprintf(stderr, "All nodes have replied\n");
58         return;
59     }
60 
61     if (rb->rc != LCB_SUCCESS) {
62         fprintf(stderr, "Failed to observe key from node. 0x%x (%s)\n",
63             rb->rc, lcb_strerror(instance, rb->rc));
64         obs_info->nresp++;
65         return;
66     }
67 
68     /* Copy over the fields we care about */
69     ni->cas = resp->cas;
70     ni->status = resp->status;
71     ni->master = resp->ismaster;
72 
73     /* Increase the response counter */
74     obs_info->nresp++;
75 }
76 
main(int argc,char * argv[])77 int main(int argc, char *argv[])
78 {
79     lcb_t instance;
80     lcb_error_t err;
81     lcb_CMDOBSERVE cmd = { 0 };
82     lcb_MULTICMD_CTX *mctx = NULL;
83     observe_info obs_info;
84     unsigned nservers, ii;
85     struct lcb_create_st create_options = { 0 };
86 
87     if (argc < 2) {
88         fail("Requires key as argument\n"
89              "Usage: observe KEY [CONNSTRING [ PASSWORD [ USERNAME ] ] ]\n");
90     }
91     create_options.version = 3;
92     if (argc > 2) {
93         create_options.v.v3.connstr = argv[2];
94     }
95     if (argc > 3) {
96         create_options.v.v3.passwd = argv[3];
97     }
98     if (argc > 4) {
99         create_options.v.v3.username = argv[4];
100     }
101 
102     if ((err = lcb_create(&instance, &create_options)) != LCB_SUCCESS) {
103         fail2("cannot create connection instance", err);
104     }
105     if ((err = lcb_connect(instance)) != LCB_SUCCESS) {
106         fail2("Couldn't schedule connection", err);
107     }
108     lcb_wait(instance);
109     if ((err = lcb_get_bootstrap_status(instance)) != LCB_SUCCESS) {
110         fail2("Couldn't get initial cluster configuration", err);
111     }
112     lcb_install_callback3(instance, LCB_CALLBACK_OBSERVE, observe_callback);
113 
114     nservers = lcb_get_num_nodes(instance);
115     obs_info.nodeinfo = calloc(nservers, sizeof (*obs_info.nodeinfo));
116     obs_info.nresp = 0;
117 
118     mctx = lcb_observe3_ctxnew(instance);
119     LCB_CMD_SET_KEY(&cmd, argv[1], strlen(argv[1]));
120     mctx->addcmd(mctx, (const lcb_CMDBASE*)&cmd);
121 
122     printf("observing the state of '%s':\n", argv[1]);
123     if ((err = mctx->done(mctx, &obs_info)) != LCB_SUCCESS) {
124         fail2("Couldn't schedule observe request", err);
125     }
126 
127     lcb_wait(instance);
128     for (ii = 0; ii < obs_info.nresp; ii++) {
129         node_info *ni = &obs_info.nodeinfo[ii];
130         fprintf(stderr, "Got status from %s node:\n", ni->master ? "master" : "replica");
131         fprintf(stderr, "\tCAS: 0x0%llx\n", ni->cas);
132         fprintf(stderr, "\tStatus (RAW): 0x%02x\n", ni->status);
133         fprintf(stderr, "\tExists [CACHE]: %s\n", ni->status & LCB_OBSERVE_NOT_FOUND ? "No" : "Yes");
134         fprintf(stderr, "\tExists [DISK]: %s\n", ni->status & LCB_OBSERVE_PERSISTED ? "Yes" : "No");
135         fprintf(stderr, "\n");
136     }
137 
138     /* The next example shows how to use lcb_observe() to only request the
139      * CAS from the master node */
140     obs_info.nresp = 0;
141     memset(obs_info.nodeinfo, 0, sizeof(obs_info.nodeinfo[0]) * nservers);
142 
143     fprintf(stderr, "Will request CAS from master...\n");
144     cmd.cmdflags |= LCB_CMDOBSERVE_F_MASTER_ONLY;
145     mctx = lcb_observe3_ctxnew(instance);
146     mctx->addcmd(mctx, (const lcb_CMDBASE*)&cmd);
147     if ((err = mctx->done(mctx, &obs_info)) != LCB_SUCCESS) {
148         fail2("Couldn't schedule observe request!\n", err);
149     }
150 
151     lcb_wait(instance);
152 
153     assert(obs_info.nresp == 1 && obs_info.nodeinfo[0].master);
154     fprintf(stderr, "CAS on master is 0x%llx\n", obs_info.nodeinfo[0].cas);
155 
156     lcb_destroy(instance);
157     free(obs_info.nodeinfo);
158     return EXIT_SUCCESS;
159 }
160