]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | ============== |
2 | Librados (C) | |
3 | ============== | |
4 | ||
5 | .. highlight:: c | |
6 | ||
7 | `librados` provides low-level access to the RADOS service. For an | |
8 | overview of RADOS, see :doc:`../../architecture`. | |
9 | ||
10 | ||
11 | Example: connecting and writing an object | |
12 | ========================================= | |
13 | ||
14 | To use `Librados`, you instantiate a :c:type:`rados_t` variable (a cluster handle) and | |
15 | call :c:func:`rados_create()` with a pointer to it:: | |
16 | ||
17 | int err; | |
18 | rados_t cluster; | |
19 | ||
20 | err = rados_create(&cluster, NULL); | |
21 | if (err < 0) { | |
22 | fprintf(stderr, "%s: cannot create a cluster handle: %s\n", argv[0], strerror(-err)); | |
23 | exit(1); | |
24 | } | |
25 | ||
26 | Then you configure your :c:type:`rados_t` to connect to your cluster, | |
27 | either by setting individual values (:c:func:`rados_conf_set()`), | |
28 | using a configuration file (:c:func:`rados_conf_read_file()`), using | |
29 | command line options (:c:func:`rados_conf_parse_argv`), or an | |
30 | environment variable (:c:func:`rados_conf_parse_env()`):: | |
31 | ||
32 | err = rados_conf_read_file(cluster, "/path/to/myceph.conf"); | |
33 | if (err < 0) { | |
34 | fprintf(stderr, "%s: cannot read config file: %s\n", argv[0], strerror(-err)); | |
35 | exit(1); | |
36 | } | |
37 | ||
38 | Once the cluster handle is configured, you can connect to the cluster with :c:func:`rados_connect()`:: | |
39 | ||
40 | err = rados_connect(cluster); | |
41 | if (err < 0) { | |
42 | fprintf(stderr, "%s: cannot connect to cluster: %s\n", argv[0], strerror(-err)); | |
43 | exit(1); | |
44 | } | |
45 | ||
46 | Then you open an "IO context", a :c:type:`rados_ioctx_t`, with :c:func:`rados_ioctx_create()`:: | |
47 | ||
48 | rados_ioctx_t io; | |
49 | char *poolname = "mypool"; | |
50 | ||
51 | err = rados_ioctx_create(cluster, poolname, &io); | |
52 | if (err < 0) { | |
53 | fprintf(stderr, "%s: cannot open rados pool %s: %s\n", argv[0], poolname, strerror(-err)); | |
54 | rados_shutdown(cluster); | |
55 | exit(1); | |
56 | } | |
57 | ||
58 | Note that the pool you try to access must exist. | |
59 | ||
60 | Then you can use the RADOS data manipulation functions, for example | |
61 | write into an object called ``greeting`` with | |
62 | :c:func:`rados_write_full()`:: | |
63 | ||
64 | err = rados_write_full(io, "greeting", "hello", 5); | |
65 | if (err < 0) { | |
66 | fprintf(stderr, "%s: cannot write pool %s: %s\n", argv[0], poolname, strerror(-err)); | |
67 | rados_ioctx_destroy(io); | |
68 | rados_shutdown(cluster); | |
69 | exit(1); | |
70 | } | |
71 | ||
c07f9fc5 | 72 | In the end, you will want to close your IO context and connection to RADOS with :c:func:`rados_ioctx_destroy()` and :c:func:`rados_shutdown()`:: |
7c673cae FG |
73 | |
74 | rados_ioctx_destroy(io); | |
75 | rados_shutdown(cluster); | |
76 | ||
77 | ||
11fdf7f2 TL |
78 | Asynchronous IO |
79 | =============== | |
7c673cae FG |
80 | |
81 | When doing lots of IO, you often don't need to wait for one operation | |
82 | to complete before starting the next one. `Librados` provides | |
83 | asynchronous versions of several operations: | |
84 | ||
85 | * :c:func:`rados_aio_write` | |
86 | * :c:func:`rados_aio_append` | |
87 | * :c:func:`rados_aio_write_full` | |
88 | * :c:func:`rados_aio_read` | |
89 | ||
90 | For each operation, you must first create a | |
91 | :c:type:`rados_completion_t` that represents what to do when the | |
92 | operation is safe or complete by calling | |
93 | :c:func:`rados_aio_create_completion`. If you don't need anything | |
94 | special to happen, you can pass NULL:: | |
95 | ||
96 | rados_completion_t comp; | |
97 | err = rados_aio_create_completion(NULL, NULL, NULL, &comp); | |
98 | if (err < 0) { | |
99 | fprintf(stderr, "%s: could not create aio completion: %s\n", argv[0], strerror(-err)); | |
100 | rados_ioctx_destroy(io); | |
101 | rados_shutdown(cluster); | |
102 | exit(1); | |
103 | } | |
104 | ||
105 | Now you can call any of the aio operations, and wait for it to | |
106 | be in memory or on disk on all replicas:: | |
107 | ||
108 | err = rados_aio_write(io, "foo", comp, "bar", 3, 0); | |
109 | if (err < 0) { | |
110 | fprintf(stderr, "%s: could not schedule aio write: %s\n", argv[0], strerror(-err)); | |
111 | rados_aio_release(comp); | |
112 | rados_ioctx_destroy(io); | |
113 | rados_shutdown(cluster); | |
114 | exit(1); | |
115 | } | |
116 | rados_aio_wait_for_complete(comp); // in memory | |
117 | rados_aio_wait_for_safe(comp); // on disk | |
118 | ||
119 | Finally, we need to free the memory used by the completion with :c:func:`rados_aio_release`:: | |
120 | ||
121 | rados_aio_release(comp); | |
122 | ||
123 | You can use the callbacks to tell your application when writes are | |
124 | durable, or when read buffers are full. For example, if you wanted to | |
125 | measure the latency of each operation when appending to several | |
126 | objects, you could schedule several writes and store the ack and | |
127 | commit time in the corresponding callback, then wait for all of them | |
128 | to complete using :c:func:`rados_aio_flush` before analyzing the | |
129 | latencies:: | |
130 | ||
131 | typedef struct { | |
132 | struct timeval start; | |
133 | struct timeval ack_end; | |
134 | struct timeval commit_end; | |
135 | } req_duration; | |
136 | ||
137 | void ack_callback(rados_completion_t comp, void *arg) { | |
138 | req_duration *dur = (req_duration *) arg; | |
139 | gettimeofday(&dur->ack_end, NULL); | |
140 | } | |
141 | ||
142 | void commit_callback(rados_completion_t comp, void *arg) { | |
143 | req_duration *dur = (req_duration *) arg; | |
144 | gettimeofday(&dur->commit_end, NULL); | |
145 | } | |
146 | ||
147 | int output_append_latency(rados_ioctx_t io, const char *data, size_t len, size_t num_writes) { | |
148 | req_duration times[num_writes]; | |
149 | rados_completion_t comps[num_writes]; | |
150 | for (size_t i = 0; i < num_writes; ++i) { | |
151 | gettimeofday(×[i].start, NULL); | |
152 | int err = rados_aio_create_completion((void*) ×[i], ack_callback, commit_callback, &comps[i]); | |
153 | if (err < 0) { | |
154 | fprintf(stderr, "Error creating rados completion: %s\n", strerror(-err)); | |
155 | return err; | |
156 | } | |
157 | char obj_name[100]; | |
158 | snprintf(obj_name, sizeof(obj_name), "foo%ld", (unsigned long)i); | |
159 | err = rados_aio_append(io, obj_name, comps[i], data, len); | |
160 | if (err < 0) { | |
161 | fprintf(stderr, "Error from rados_aio_append: %s", strerror(-err)); | |
162 | return err; | |
163 | } | |
164 | } | |
165 | // wait until all requests finish *and* the callbacks complete | |
166 | rados_aio_flush(io); | |
167 | // the latencies can now be analyzed | |
168 | printf("Request # | Ack latency (s) | Commit latency (s)\n"); | |
169 | for (size_t i = 0; i < num_writes; ++i) { | |
170 | // don't forget to free the completions | |
171 | rados_aio_release(comps[i]); | |
172 | struct timeval ack_lat, commit_lat; | |
173 | timersub(×[i].ack_end, ×[i].start, &ack_lat); | |
174 | timersub(×[i].commit_end, ×[i].start, &commit_lat); | |
175 | printf("%9ld | %8ld.%06ld | %10ld.%06ld\n", (unsigned long) i, ack_lat.tv_sec, ack_lat.tv_usec, commit_lat.tv_sec, commit_lat.tv_usec); | |
176 | } | |
177 | return 0; | |
178 | } | |
179 | ||
180 | Note that all the :c:type:`rados_completion_t` must be freed with :c:func:`rados_aio_release` to avoid leaking memory. | |
181 | ||
182 | ||
183 | API calls | |
184 | ========= | |
185 | ||
186 | .. autodoxygenfile:: rados_types.h | |
187 | .. autodoxygenfile:: librados.h |