├── .gitignore
├── LICENSE
├── README.md
├── Vagrantfile
└── src
├── redismodule.h
└── scache
├── Makefile
└── scache.c
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Object files
5 | *.o
6 | *.xo
7 | *.ko
8 | *.obj
9 | *.elf
10 |
11 | # Linker output
12 | *.ilk
13 | *.map
14 | *.exp
15 |
16 | # Precompiled Headers
17 | *.gch
18 | *.pch
19 |
20 | # Libraries
21 | *.lib
22 | *.a
23 | *.la
24 | *.lo
25 |
26 | # Shared objects (inc. Windows DLLs)
27 | *.dll
28 | *.so
29 | *.so.*
30 | *.dylib
31 |
32 | # Executables
33 | *.exe
34 | *.out
35 | *.app
36 | *.i*86
37 | *.x86_64
38 | *.hex
39 |
40 | # Debug files
41 | *.dSYM/
42 | *.su
43 | *.idb
44 | *.pdb
45 |
46 | # Kernel Module Compile Results
47 | *.mod*
48 | *.cmd
49 | .tmp_versions/
50 | modules.order
51 | Module.symvers
52 | Mkfile.old
53 | dkms.conf
54 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU AFFERO GENERAL PUBLIC LICENSE
2 | Version 3, 19 November 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU Affero General Public License is a free, copyleft license for
11 | software and other kinds of works, specifically designed to ensure
12 | cooperation with the community in the case of network server software.
13 |
14 | The licenses for most software and other practical works are designed
15 | to take away your freedom to share and change the works. By contrast,
16 | our General Public Licenses are intended to guarantee your freedom to
17 | share and change all versions of a program--to make sure it remains free
18 | software for all its users.
19 |
20 | When we speak of free software, we are referring to freedom, not
21 | price. Our General Public Licenses are designed to make sure that you
22 | have the freedom to distribute copies of free software (and charge for
23 | them if you wish), that you receive source code or can get it if you
24 | want it, that you can change the software or use pieces of it in new
25 | free programs, and that you know you can do these things.
26 |
27 | Developers that use our General Public Licenses protect your rights
28 | with two steps: (1) assert copyright on the software, and (2) offer
29 | you this License which gives you legal permission to copy, distribute
30 | and/or modify the software.
31 |
32 | A secondary benefit of defending all users' freedom is that
33 | improvements made in alternate versions of the program, if they
34 | receive widespread use, become available for other developers to
35 | incorporate. Many developers of free software are heartened and
36 | encouraged by the resulting cooperation. However, in the case of
37 | software used on network servers, this result may fail to come about.
38 | The GNU General Public License permits making a modified version and
39 | letting the public access it on a server without ever releasing its
40 | source code to the public.
41 |
42 | The GNU Affero General Public License is designed specifically to
43 | ensure that, in such cases, the modified source code becomes available
44 | to the community. It requires the operator of a network server to
45 | provide the source code of the modified version running there to the
46 | users of that server. Therefore, public use of a modified version, on
47 | a publicly accessible server, gives the public access to the source
48 | code of the modified version.
49 |
50 | An older license, called the Affero General Public License and
51 | published by Affero, was designed to accomplish similar goals. This is
52 | a different license, not a version of the Affero GPL, but Affero has
53 | released a new version of the Affero GPL which permits relicensing under
54 | this license.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | TERMS AND CONDITIONS
60 |
61 | 0. Definitions.
62 |
63 | "This License" refers to version 3 of the GNU Affero General Public License.
64 |
65 | "Copyright" also means copyright-like laws that apply to other kinds of
66 | works, such as semiconductor masks.
67 |
68 | "The Program" refers to any copyrightable work licensed under this
69 | License. Each licensee is addressed as "you". "Licensees" and
70 | "recipients" may be individuals or organizations.
71 |
72 | To "modify" a work means to copy from or adapt all or part of the work
73 | in a fashion requiring copyright permission, other than the making of an
74 | exact copy. The resulting work is called a "modified version" of the
75 | earlier work or a work "based on" the earlier work.
76 |
77 | A "covered work" means either the unmodified Program or a work based
78 | on the Program.
79 |
80 | To "propagate" a work means to do anything with it that, without
81 | permission, would make you directly or secondarily liable for
82 | infringement under applicable copyright law, except executing it on a
83 | computer or modifying a private copy. Propagation includes copying,
84 | distribution (with or without modification), making available to the
85 | public, and in some countries other activities as well.
86 |
87 | To "convey" a work means any kind of propagation that enables other
88 | parties to make or receive copies. Mere interaction with a user through
89 | a computer network, with no transfer of a copy, is not conveying.
90 |
91 | An interactive user interface displays "Appropriate Legal Notices"
92 | to the extent that it includes a convenient and prominently visible
93 | feature that (1) displays an appropriate copyright notice, and (2)
94 | tells the user that there is no warranty for the work (except to the
95 | extent that warranties are provided), that licensees may convey the
96 | work under this License, and how to view a copy of this License. If
97 | the interface presents a list of user commands or options, such as a
98 | menu, a prominent item in the list meets this criterion.
99 |
100 | 1. Source Code.
101 |
102 | The "source code" for a work means the preferred form of the work
103 | for making modifications to it. "Object code" means any non-source
104 | form of a work.
105 |
106 | A "Standard Interface" means an interface that either is an official
107 | standard defined by a recognized standards body, or, in the case of
108 | interfaces specified for a particular programming language, one that
109 | is widely used among developers working in that language.
110 |
111 | The "System Libraries" of an executable work include anything, other
112 | than the work as a whole, that (a) is included in the normal form of
113 | packaging a Major Component, but which is not part of that Major
114 | Component, and (b) serves only to enable use of the work with that
115 | Major Component, or to implement a Standard Interface for which an
116 | implementation is available to the public in source code form. A
117 | "Major Component", in this context, means a major essential component
118 | (kernel, window system, and so on) of the specific operating system
119 | (if any) on which the executable work runs, or a compiler used to
120 | produce the work, or an object code interpreter used to run it.
121 |
122 | The "Corresponding Source" for a work in object code form means all
123 | the source code needed to generate, install, and (for an executable
124 | work) run the object code and to modify the work, including scripts to
125 | control those activities. However, it does not include the work's
126 | System Libraries, or general-purpose tools or generally available free
127 | programs which are used unmodified in performing those activities but
128 | which are not part of the work. For example, Corresponding Source
129 | includes interface definition files associated with source files for
130 | the work, and the source code for shared libraries and dynamically
131 | linked subprograms that the work is specifically designed to require,
132 | such as by intimate data communication or control flow between those
133 | subprograms and other parts of the work.
134 |
135 | The Corresponding Source need not include anything that users
136 | can regenerate automatically from other parts of the Corresponding
137 | Source.
138 |
139 | The Corresponding Source for a work in source code form is that
140 | same work.
141 |
142 | 2. Basic Permissions.
143 |
144 | All rights granted under this License are granted for the term of
145 | copyright on the Program, and are irrevocable provided the stated
146 | conditions are met. This License explicitly affirms your unlimited
147 | permission to run the unmodified Program. The output from running a
148 | covered work is covered by this License only if the output, given its
149 | content, constitutes a covered work. This License acknowledges your
150 | rights of fair use or other equivalent, as provided by copyright law.
151 |
152 | You may make, run and propagate covered works that you do not
153 | convey, without conditions so long as your license otherwise remains
154 | in force. You may convey covered works to others for the sole purpose
155 | of having them make modifications exclusively for you, or provide you
156 | with facilities for running those works, provided that you comply with
157 | the terms of this License in conveying all material for which you do
158 | not control copyright. Those thus making or running the covered works
159 | for you must do so exclusively on your behalf, under your direction
160 | and control, on terms that prohibit them from making any copies of
161 | your copyrighted material outside their relationship with you.
162 |
163 | Conveying under any other circumstances is permitted solely under
164 | the conditions stated below. Sublicensing is not allowed; section 10
165 | makes it unnecessary.
166 |
167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168 |
169 | No covered work shall be deemed part of an effective technological
170 | measure under any applicable law fulfilling obligations under article
171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
172 | similar laws prohibiting or restricting circumvention of such
173 | measures.
174 |
175 | When you convey a covered work, you waive any legal power to forbid
176 | circumvention of technological measures to the extent such circumvention
177 | is effected by exercising rights under this License with respect to
178 | the covered work, and you disclaim any intention to limit operation or
179 | modification of the work as a means of enforcing, against the work's
180 | users, your or third parties' legal rights to forbid circumvention of
181 | technological measures.
182 |
183 | 4. Conveying Verbatim Copies.
184 |
185 | You may convey verbatim copies of the Program's source code as you
186 | receive it, in any medium, provided that you conspicuously and
187 | appropriately publish on each copy an appropriate copyright notice;
188 | keep intact all notices stating that this License and any
189 | non-permissive terms added in accord with section 7 apply to the code;
190 | keep intact all notices of the absence of any warranty; and give all
191 | recipients a copy of this License along with the Program.
192 |
193 | You may charge any price or no price for each copy that you convey,
194 | and you may offer support or warranty protection for a fee.
195 |
196 | 5. Conveying Modified Source Versions.
197 |
198 | You may convey a work based on the Program, or the modifications to
199 | produce it from the Program, in the form of source code under the
200 | terms of section 4, provided that you also meet all of these conditions:
201 |
202 | a) The work must carry prominent notices stating that you modified
203 | it, and giving a relevant date.
204 |
205 | b) The work must carry prominent notices stating that it is
206 | released under this License and any conditions added under section
207 | 7. This requirement modifies the requirement in section 4 to
208 | "keep intact all notices".
209 |
210 | c) You must license the entire work, as a whole, under this
211 | License to anyone who comes into possession of a copy. This
212 | License will therefore apply, along with any applicable section 7
213 | additional terms, to the whole of the work, and all its parts,
214 | regardless of how they are packaged. This License gives no
215 | permission to license the work in any other way, but it does not
216 | invalidate such permission if you have separately received it.
217 |
218 | d) If the work has interactive user interfaces, each must display
219 | Appropriate Legal Notices; however, if the Program has interactive
220 | interfaces that do not display Appropriate Legal Notices, your
221 | work need not make them do so.
222 |
223 | A compilation of a covered work with other separate and independent
224 | works, which are not by their nature extensions of the covered work,
225 | and which are not combined with it such as to form a larger program,
226 | in or on a volume of a storage or distribution medium, is called an
227 | "aggregate" if the compilation and its resulting copyright are not
228 | used to limit the access or legal rights of the compilation's users
229 | beyond what the individual works permit. Inclusion of a covered work
230 | in an aggregate does not cause this License to apply to the other
231 | parts of the aggregate.
232 |
233 | 6. Conveying Non-Source Forms.
234 |
235 | You may convey a covered work in object code form under the terms
236 | of sections 4 and 5, provided that you also convey the
237 | machine-readable Corresponding Source under the terms of this License,
238 | in one of these ways:
239 |
240 | a) Convey the object code in, or embodied in, a physical product
241 | (including a physical distribution medium), accompanied by the
242 | Corresponding Source fixed on a durable physical medium
243 | customarily used for software interchange.
244 |
245 | b) Convey the object code in, or embodied in, a physical product
246 | (including a physical distribution medium), accompanied by a
247 | written offer, valid for at least three years and valid for as
248 | long as you offer spare parts or customer support for that product
249 | model, to give anyone who possesses the object code either (1) a
250 | copy of the Corresponding Source for all the software in the
251 | product that is covered by this License, on a durable physical
252 | medium customarily used for software interchange, for a price no
253 | more than your reasonable cost of physically performing this
254 | conveying of source, or (2) access to copy the
255 | Corresponding Source from a network server at no charge.
256 |
257 | c) Convey individual copies of the object code with a copy of the
258 | written offer to provide the Corresponding Source. This
259 | alternative is allowed only occasionally and noncommercially, and
260 | only if you received the object code with such an offer, in accord
261 | with subsection 6b.
262 |
263 | d) Convey the object code by offering access from a designated
264 | place (gratis or for a charge), and offer equivalent access to the
265 | Corresponding Source in the same way through the same place at no
266 | further charge. You need not require recipients to copy the
267 | Corresponding Source along with the object code. If the place to
268 | copy the object code is a network server, the Corresponding Source
269 | may be on a different server (operated by you or a third party)
270 | that supports equivalent copying facilities, provided you maintain
271 | clear directions next to the object code saying where to find the
272 | Corresponding Source. Regardless of what server hosts the
273 | Corresponding Source, you remain obligated to ensure that it is
274 | available for as long as needed to satisfy these requirements.
275 |
276 | e) Convey the object code using peer-to-peer transmission, provided
277 | you inform other peers where the object code and Corresponding
278 | Source of the work are being offered to the general public at no
279 | charge under subsection 6d.
280 |
281 | A separable portion of the object code, whose source code is excluded
282 | from the Corresponding Source as a System Library, need not be
283 | included in conveying the object code work.
284 |
285 | A "User Product" is either (1) a "consumer product", which means any
286 | tangible personal property which is normally used for personal, family,
287 | or household purposes, or (2) anything designed or sold for incorporation
288 | into a dwelling. In determining whether a product is a consumer product,
289 | doubtful cases shall be resolved in favor of coverage. For a particular
290 | product received by a particular user, "normally used" refers to a
291 | typical or common use of that class of product, regardless of the status
292 | of the particular user or of the way in which the particular user
293 | actually uses, or expects or is expected to use, the product. A product
294 | is a consumer product regardless of whether the product has substantial
295 | commercial, industrial or non-consumer uses, unless such uses represent
296 | the only significant mode of use of the product.
297 |
298 | "Installation Information" for a User Product means any methods,
299 | procedures, authorization keys, or other information required to install
300 | and execute modified versions of a covered work in that User Product from
301 | a modified version of its Corresponding Source. The information must
302 | suffice to ensure that the continued functioning of the modified object
303 | code is in no case prevented or interfered with solely because
304 | modification has been made.
305 |
306 | If you convey an object code work under this section in, or with, or
307 | specifically for use in, a User Product, and the conveying occurs as
308 | part of a transaction in which the right of possession and use of the
309 | User Product is transferred to the recipient in perpetuity or for a
310 | fixed term (regardless of how the transaction is characterized), the
311 | Corresponding Source conveyed under this section must be accompanied
312 | by the Installation Information. But this requirement does not apply
313 | if neither you nor any third party retains the ability to install
314 | modified object code on the User Product (for example, the work has
315 | been installed in ROM).
316 |
317 | The requirement to provide Installation Information does not include a
318 | requirement to continue to provide support service, warranty, or updates
319 | for a work that has been modified or installed by the recipient, or for
320 | the User Product in which it has been modified or installed. Access to a
321 | network may be denied when the modification itself materially and
322 | adversely affects the operation of the network or violates the rules and
323 | protocols for communication across the network.
324 |
325 | Corresponding Source conveyed, and Installation Information provided,
326 | in accord with this section must be in a format that is publicly
327 | documented (and with an implementation available to the public in
328 | source code form), and must require no special password or key for
329 | unpacking, reading or copying.
330 |
331 | 7. Additional Terms.
332 |
333 | "Additional permissions" are terms that supplement the terms of this
334 | License by making exceptions from one or more of its conditions.
335 | Additional permissions that are applicable to the entire Program shall
336 | be treated as though they were included in this License, to the extent
337 | that they are valid under applicable law. If additional permissions
338 | apply only to part of the Program, that part may be used separately
339 | under those permissions, but the entire Program remains governed by
340 | this License without regard to the additional permissions.
341 |
342 | When you convey a copy of a covered work, you may at your option
343 | remove any additional permissions from that copy, or from any part of
344 | it. (Additional permissions may be written to require their own
345 | removal in certain cases when you modify the work.) You may place
346 | additional permissions on material, added by you to a covered work,
347 | for which you have or can give appropriate copyright permission.
348 |
349 | Notwithstanding any other provision of this License, for material you
350 | add to a covered work, you may (if authorized by the copyright holders of
351 | that material) supplement the terms of this License with terms:
352 |
353 | a) Disclaiming warranty or limiting liability differently from the
354 | terms of sections 15 and 16 of this License; or
355 |
356 | b) Requiring preservation of specified reasonable legal notices or
357 | author attributions in that material or in the Appropriate Legal
358 | Notices displayed by works containing it; or
359 |
360 | c) Prohibiting misrepresentation of the origin of that material, or
361 | requiring that modified versions of such material be marked in
362 | reasonable ways as different from the original version; or
363 |
364 | d) Limiting the use for publicity purposes of names of licensors or
365 | authors of the material; or
366 |
367 | e) Declining to grant rights under trademark law for use of some
368 | trade names, trademarks, or service marks; or
369 |
370 | f) Requiring indemnification of licensors and authors of that
371 | material by anyone who conveys the material (or modified versions of
372 | it) with contractual assumptions of liability to the recipient, for
373 | any liability that these contractual assumptions directly impose on
374 | those licensors and authors.
375 |
376 | All other non-permissive additional terms are considered "further
377 | restrictions" within the meaning of section 10. If the Program as you
378 | received it, or any part of it, contains a notice stating that it is
379 | governed by this License along with a term that is a further
380 | restriction, you may remove that term. If a license document contains
381 | a further restriction but permits relicensing or conveying under this
382 | License, you may add to a covered work material governed by the terms
383 | of that license document, provided that the further restriction does
384 | not survive such relicensing or conveying.
385 |
386 | If you add terms to a covered work in accord with this section, you
387 | must place, in the relevant source files, a statement of the
388 | additional terms that apply to those files, or a notice indicating
389 | where to find the applicable terms.
390 |
391 | Additional terms, permissive or non-permissive, may be stated in the
392 | form of a separately written license, or stated as exceptions;
393 | the above requirements apply either way.
394 |
395 | 8. Termination.
396 |
397 | You may not propagate or modify a covered work except as expressly
398 | provided under this License. Any attempt otherwise to propagate or
399 | modify it is void, and will automatically terminate your rights under
400 | this License (including any patent licenses granted under the third
401 | paragraph of section 11).
402 |
403 | However, if you cease all violation of this License, then your
404 | license from a particular copyright holder is reinstated (a)
405 | provisionally, unless and until the copyright holder explicitly and
406 | finally terminates your license, and (b) permanently, if the copyright
407 | holder fails to notify you of the violation by some reasonable means
408 | prior to 60 days after the cessation.
409 |
410 | Moreover, your license from a particular copyright holder is
411 | reinstated permanently if the copyright holder notifies you of the
412 | violation by some reasonable means, this is the first time you have
413 | received notice of violation of this License (for any work) from that
414 | copyright holder, and you cure the violation prior to 30 days after
415 | your receipt of the notice.
416 |
417 | Termination of your rights under this section does not terminate the
418 | licenses of parties who have received copies or rights from you under
419 | this License. If your rights have been terminated and not permanently
420 | reinstated, you do not qualify to receive new licenses for the same
421 | material under section 10.
422 |
423 | 9. Acceptance Not Required for Having Copies.
424 |
425 | You are not required to accept this License in order to receive or
426 | run a copy of the Program. Ancillary propagation of a covered work
427 | occurring solely as a consequence of using peer-to-peer transmission
428 | to receive a copy likewise does not require acceptance. However,
429 | nothing other than this License grants you permission to propagate or
430 | modify any covered work. These actions infringe copyright if you do
431 | not accept this License. Therefore, by modifying or propagating a
432 | covered work, you indicate your acceptance of this License to do so.
433 |
434 | 10. Automatic Licensing of Downstream Recipients.
435 |
436 | Each time you convey a covered work, the recipient automatically
437 | receives a license from the original licensors, to run, modify and
438 | propagate that work, subject to this License. You are not responsible
439 | for enforcing compliance by third parties with this License.
440 |
441 | An "entity transaction" is a transaction transferring control of an
442 | organization, or substantially all assets of one, or subdividing an
443 | organization, or merging organizations. If propagation of a covered
444 | work results from an entity transaction, each party to that
445 | transaction who receives a copy of the work also receives whatever
446 | licenses to the work the party's predecessor in interest had or could
447 | give under the previous paragraph, plus a right to possession of the
448 | Corresponding Source of the work from the predecessor in interest, if
449 | the predecessor has it or can get it with reasonable efforts.
450 |
451 | You may not impose any further restrictions on the exercise of the
452 | rights granted or affirmed under this License. For example, you may
453 | not impose a license fee, royalty, or other charge for exercise of
454 | rights granted under this License, and you may not initiate litigation
455 | (including a cross-claim or counterclaim in a lawsuit) alleging that
456 | any patent claim is infringed by making, using, selling, offering for
457 | sale, or importing the Program or any portion of it.
458 |
459 | 11. Patents.
460 |
461 | A "contributor" is a copyright holder who authorizes use under this
462 | License of the Program or a work on which the Program is based. The
463 | work thus licensed is called the contributor's "contributor version".
464 |
465 | A contributor's "essential patent claims" are all patent claims
466 | owned or controlled by the contributor, whether already acquired or
467 | hereafter acquired, that would be infringed by some manner, permitted
468 | by this License, of making, using, or selling its contributor version,
469 | but do not include claims that would be infringed only as a
470 | consequence of further modification of the contributor version. For
471 | purposes of this definition, "control" includes the right to grant
472 | patent sublicenses in a manner consistent with the requirements of
473 | this License.
474 |
475 | Each contributor grants you a non-exclusive, worldwide, royalty-free
476 | patent license under the contributor's essential patent claims, to
477 | make, use, sell, offer for sale, import and otherwise run, modify and
478 | propagate the contents of its contributor version.
479 |
480 | In the following three paragraphs, a "patent license" is any express
481 | agreement or commitment, however denominated, not to enforce a patent
482 | (such as an express permission to practice a patent or covenant not to
483 | sue for patent infringement). To "grant" such a patent license to a
484 | party means to make such an agreement or commitment not to enforce a
485 | patent against the party.
486 |
487 | If you convey a covered work, knowingly relying on a patent license,
488 | and the Corresponding Source of the work is not available for anyone
489 | to copy, free of charge and under the terms of this License, through a
490 | publicly available network server or other readily accessible means,
491 | then you must either (1) cause the Corresponding Source to be so
492 | available, or (2) arrange to deprive yourself of the benefit of the
493 | patent license for this particular work, or (3) arrange, in a manner
494 | consistent with the requirements of this License, to extend the patent
495 | license to downstream recipients. "Knowingly relying" means you have
496 | actual knowledge that, but for the patent license, your conveying the
497 | covered work in a country, or your recipient's use of the covered work
498 | in a country, would infringe one or more identifiable patents in that
499 | country that you have reason to believe are valid.
500 |
501 | If, pursuant to or in connection with a single transaction or
502 | arrangement, you convey, or propagate by procuring conveyance of, a
503 | covered work, and grant a patent license to some of the parties
504 | receiving the covered work authorizing them to use, propagate, modify
505 | or convey a specific copy of the covered work, then the patent license
506 | you grant is automatically extended to all recipients of the covered
507 | work and works based on it.
508 |
509 | A patent license is "discriminatory" if it does not include within
510 | the scope of its coverage, prohibits the exercise of, or is
511 | conditioned on the non-exercise of one or more of the rights that are
512 | specifically granted under this License. You may not convey a covered
513 | work if you are a party to an arrangement with a third party that is
514 | in the business of distributing software, under which you make payment
515 | to the third party based on the extent of your activity of conveying
516 | the work, and under which the third party grants, to any of the
517 | parties who would receive the covered work from you, a discriminatory
518 | patent license (a) in connection with copies of the covered work
519 | conveyed by you (or copies made from those copies), or (b) primarily
520 | for and in connection with specific products or compilations that
521 | contain the covered work, unless you entered into that arrangement,
522 | or that patent license was granted, prior to 28 March 2007.
523 |
524 | Nothing in this License shall be construed as excluding or limiting
525 | any implied license or other defenses to infringement that may
526 | otherwise be available to you under applicable patent law.
527 |
528 | 12. No Surrender of Others' Freedom.
529 |
530 | If conditions are imposed on you (whether by court order, agreement or
531 | otherwise) that contradict the conditions of this License, they do not
532 | excuse you from the conditions of this License. If you cannot convey a
533 | covered work so as to satisfy simultaneously your obligations under this
534 | License and any other pertinent obligations, then as a consequence you may
535 | not convey it at all. For example, if you agree to terms that obligate you
536 | to collect a royalty for further conveying from those to whom you convey
537 | the Program, the only way you could satisfy both those terms and this
538 | License would be to refrain entirely from conveying the Program.
539 |
540 | 13. Remote Network Interaction; Use with the GNU General Public License.
541 |
542 | Notwithstanding any other provision of this License, if you modify the
543 | Program, your modified version must prominently offer all users
544 | interacting with it remotely through a computer network (if your version
545 | supports such interaction) an opportunity to receive the Corresponding
546 | Source of your version by providing access to the Corresponding Source
547 | from a network server at no charge, through some standard or customary
548 | means of facilitating copying of software. This Corresponding Source
549 | shall include the Corresponding Source for any work covered by version 3
550 | of the GNU General Public License that is incorporated pursuant to the
551 | following paragraph.
552 |
553 | Notwithstanding any other provision of this License, you have
554 | permission to link or combine any covered work with a work licensed
555 | under version 3 of the GNU General Public License into a single
556 | combined work, and to convey the resulting work. The terms of this
557 | License will continue to apply to the part which is the covered work,
558 | but the work with which it is combined will remain governed by version
559 | 3 of the GNU General Public License.
560 |
561 | 14. Revised Versions of this License.
562 |
563 | The Free Software Foundation may publish revised and/or new versions of
564 | the GNU Affero General Public License from time to time. Such new versions
565 | will be similar in spirit to the present version, but may differ in detail to
566 | address new problems or concerns.
567 |
568 | Each version is given a distinguishing version number. If the
569 | Program specifies that a certain numbered version of the GNU Affero General
570 | Public License "or any later version" applies to it, you have the
571 | option of following the terms and conditions either of that numbered
572 | version or of any later version published by the Free Software
573 | Foundation. If the Program does not specify a version number of the
574 | GNU Affero General Public License, you may choose any version ever published
575 | by the Free Software Foundation.
576 |
577 | If the Program specifies that a proxy can decide which future
578 | versions of the GNU Affero General Public License can be used, that proxy's
579 | public statement of acceptance of a version permanently authorizes you
580 | to choose that version for the Program.
581 |
582 | Later license versions may give you additional or different
583 | permissions. However, no additional obligations are imposed on any
584 | author or copyright holder as a result of your choosing to follow a
585 | later version.
586 |
587 | 15. Disclaimer of Warranty.
588 |
589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597 |
598 | 16. Limitation of Liability.
599 |
600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608 | SUCH DAMAGES.
609 |
610 | 17. Interpretation of Sections 15 and 16.
611 |
612 | If the disclaimer of warranty and limitation of liability provided
613 | above cannot be given local legal effect according to their terms,
614 | reviewing courts shall apply local law that most closely approximates
615 | an absolute waiver of all civil liability in connection with the
616 | Program, unless a warranty or assumption of liability accompanies a
617 | copy of the Program in return for a fee.
618 |
619 | END OF TERMS AND CONDITIONS
620 |
621 | How to Apply These Terms to Your New Programs
622 |
623 | If you develop a new program, and you want it to be of the greatest
624 | possible use to the public, the best way to achieve this is to make it
625 | free software which everyone can redistribute and change under these terms.
626 |
627 | To do so, attach the following notices to the program. It is safest
628 | to attach them to the start of each source file to most effectively
629 | state the exclusion of warranty; and each file should have at least
630 | the "copyright" line and a pointer to where the full notice is found.
631 |
632 |
633 | Copyright (C)
634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
662 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # redismodule-smartcache
2 | Smart and autonomous cache in a redis module
3 |
4 | # Summary
5 |
6 | This modules implements a pass-through cache, or a proxy cache,
7 | or a transparent cache.
8 |
9 | # Longer description
10 |
11 | It currently only connects to MySQL database, but can be easily
12 | ported to any other database, SQL or NoSQL, to accelerate the
13 | queries or to minimize the load on the underlying database.
14 |
15 | The goal is to make the application simple, it only has to query
16 | the cache and the cache will eventually query the underlying
17 | database. No need to manage the cache and the DB connection at
18 | the application level anymore. No need to link and use the DB
19 | driver in the application anymore.
20 |
21 | One of the usecase is to accelerate the queries and minimize the
22 | latency, another usecase is to lower the load pressure on the
23 | database (making more resources available for other tasks or
24 | minimizing the costs).
25 |
26 | # Commands
27 |
28 | The module implements two sets of Redis commands. The first one
29 | is used to manage the caches, whereas the second one is used to
30 | query the caches.
31 |
32 | ## Cache management
33 |
34 | The following commands are used to administrate the caches, some
35 | kind of DDL for SCache.
36 |
37 | ### scache.create
38 |
39 | Defines a new cache and its underlying database
40 | connection. A connection is immediately openned to the
41 | database and the creation will fail if the database is
42 | not reachable.
43 |
44 | **Arguments**
45 | - *cachename* is the cache identifier. It has to be unique.
46 | - *ttl* is the default Time To Live in seconds before value expiration
47 | - *host* IP address or DNS name of the database server
48 | - *port* TCP port of the database server (usually 3306)
49 | - *user* login name to connect with to the database
50 | - *password* password to connect to the database
51 | - *schema* name of the database schema
52 |
53 | **Return value**
54 | - If the connection test succeed, returns the cache configuration (without password), otherwise returns an error.
55 |
56 | *Note*: This command does not appear in the `MONITOR` output to avoid displaying passwords.
57 |
58 | ### scache.list
59 |
60 | Lists all the defined caches
61 |
62 | **Arguments**
63 | - None
64 |
65 | **Return value**
66 | - A simple list of available cachenames.
67 |
68 | ### scache.info
69 |
70 | Gets information about one specific defined cache.
71 |
72 | **Arguments**
73 | - *cachename* Name of the cache
74 |
75 | **Return value**
76 | - If the cache exists, returns its configuration (without password), otherwise returns an error
77 |
78 | ### scache.test
79 |
80 | Test the database connection of a specific cache (in case it broke after the cache creation).
81 |
82 | **Arguments**
83 | - *cachename* Name of the cache
84 |
85 | **Return value**
86 | - "1" if the test succeed, otherwise an error.
87 |
88 | ### scache.flush
89 |
90 | Flush all the cached resultsets from a cache.
91 |
92 | **Arguments**
93 | - *cachename* Name of the cache
94 |
95 | **Return value**
96 | - Number of purged values
97 |
98 | ### scache.delete
99 |
100 | Flush a cache and delete its definition
101 |
102 | **Arguments**
103 | - *cachename* Name of the cache
104 |
105 | **Return value**
106 | - Number of purged values
107 |
108 | ## Cache querying
109 |
110 | These commands are used by the application to actually query the
111 | cache, some kind of DML.
112 |
113 | ### scache.getvalue
114 |
115 | Returns a resultset values from the cache, eventually fetching them automatically from the database.
116 |
117 | **Arguments**
118 | - *cachename* Name of the cache
119 | - *query* Underlying database query string
120 |
121 | **Return value**
122 | - A list of records, each of them is a pipe-separated column values
123 |
124 | ### scache.getmeta
125 |
126 | Returns a resultset metadata from the cache, eventually fetching them automatically from the database.
127 |
128 | **Arguments**
129 | - *cachename* Name of the cache
130 | - *query* Underlying database query string
131 |
132 | **Return value**
133 | - A list of column name / column type, pipe-separated.
134 |
135 | # Specifications
136 |
137 | The module defines caches. Each cache is currently a MySQL
138 | connection (host/port/user/password/schema) and a default TTL.
139 | Once a cache is defined, it can be queried with SQL queries, if
140 | it does not already have the resultset, it blocks the client and
141 | execute the SQL query against MySQL in a thread (to avoid
142 | blocking Redis and make the query asynchronous), store the
143 | result set in Redis with a TTL. At the end, it returns the
144 | resultset to the client.
145 |
146 | The cache definition has to be stored in a Redis datastructure
147 | to have the benefit of easy persistency and replication across a
148 | cluster nodes, but the connection handle has to be stored in the
149 | node memory, in internal datastructure as it is specific to a
150 | single instance. We keep the connection handle to avoid
151 | opening/closing connections and we use the auto-reconnect MySQL
152 | feature to keep the connection always ready for queries.
153 |
154 | The resultsets don't have to be replicated across the cluster
155 | and don't have to be persisted, neither. Thus, we store them in
156 | an internal datastructure. We try to leverave the Redis TTL to
157 | expire our resultset. For each dataset, we create a key in a
158 | Redis data structure, with a TTL. The module starts a thread
159 | that subscribe to the notification channel to be notified when a
160 | key expires and to delete the related resultset from internal
161 | datastructures.
162 |
163 | # Build instructions
164 |
165 | ## Prerequisites
166 |
167 | Install mysqlclient development libraries.
168 |
169 | ```
170 | sudo aptitude install libmysqlclient-dev
171 | ```
172 |
173 | ## Compile
174 |
175 | ```
176 | cd src/scache
177 | make
178 | ```
179 |
180 | # Test
181 |
182 | ## Prerequisites
183 |
184 | Install mysql server :
185 |
186 | ```
187 | sudo aptitude install libmysqlclient-dev mysql-server
188 | ```
189 |
190 | Connect to mysql server using the CLI client and the admin account :
191 |
192 | ```
193 | mysql -u root
194 | ```
195 |
196 | Create the test database, the test user and grant him all permissions on the database :
197 |
198 | ``` sql
199 | create database redisdb;
200 | create user redisuser@'%' identified by 'redispassword';
201 | grant all privileges on redisdb.* to redisuser;
202 | ```
203 |
204 | Create some test values
205 |
206 | ``` sql
207 | use redisdb;
208 | create table customer (id integer, Nom varchar(255), prenom varchar(255), `date de naissance` datetime);
209 | insert into customer values (1,"Cerbelle","François","2017-05-26");
210 | insert into customer values (2,"Carbonnel","Georges","1970-01-01");
211 | insert into customer values (3,"Sanfilippo","Salvatore","1970-01-01");
212 | ```
213 |
214 | ## Testcase
215 |
216 | You can use a second redis-cli instance with `MONITOR` and `tail -f /var/log/redis*.log`
217 |
218 | Start Redis client :
219 |
220 | ```
221 | redis-cli
222 | ```
223 |
224 | Load the module :
225 |
226 | ```
227 | module load /home/vagrant/DownloadCache/redismodule-smartcache/src/scache/scache.so
228 | ```
229 |
230 | Enjoy:
231 |
232 | ```
233 | scache.list
234 | scache.create cache1 20 127.0.0.1 3306 redisdb redisuser redispassword
235 | scache.create cache2 10 localhost 3306 redisdb redisuser redispassword
236 | scache.create cache3 5 node4.vm 3306 redisdb redisuser redispassword
237 | scache.list
238 | scache.info cache2
239 | scache.delete cache2
240 | scache.list
241 | scache.getvalue cache1 'select * from customer'
242 | ```
243 |
244 | # Benchmark
245 |
246 | This is a very quick benchmark. It is impacted by the binaries implementations, but basically, both tests suffers from the same constraints : fork a process, read a program
247 | from disk and execute it, open a connection to the source (mysql or redis), execute the same query, get the same resultset. I added an extra space in the second query to avoid
248 | using the previously cached resultset in MySQL's internal cache.
249 |
250 | ```
251 | i=0; time while [ $i -lt 10000 ]; do echo "select * from customer" | mysql -u redisuser -predispassword redisdb > /dev/null ; i=$((i+1)); done; uptime
252 |
253 | i=0; time while [ $i -lt 10000 ]; do redis-cli 'scache.getvalue cache1 "select * from customer"' > /dev/null ; i=$((i+1)); done; uptime
254 | ```
255 |
256 | The same test, using one connection and sending 1000000 commands
257 |
258 | ```
259 | i=0; time while [ $i -lt 10000 ]; do echo "select * from customer;"; i=$((i+1)); done | mysql -u redisuser -predispassword redisdb > /dev/null; uptime
260 |
261 | i=0; time while [ $i -lt 10000 ]; do echo 'scache.getvalue cache1 "select * from customer"' ; i=$((i+1)); done | redis-cli > /dev/null ; uptime
262 |
263 | ```
264 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | # -*- mode: ruby -*-
2 | # vi: set ft=ruby :
3 |
4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure
5 | # configures the configuration version (we support older styles for
6 | # backwards compatibility). Please don't change it unless you know what
7 | # you're doing.
8 | Vagrant.configure("2") do |config|
9 | # The most common configuration options are documented and commented below.
10 | # For a complete reference, please see the online documentation at
11 | # https://docs.vagrantup.com.
12 |
13 | # Every Vagrant development environment requires a box. You can search for
14 | # boxes at https://atlas.hashicorp.com/search.
15 | config.vm.box = "ubuntu/trusty64"
16 |
17 | # Disable automatic box update checking. If you disable this, then
18 | # boxes will only be checked for updates when the user runs
19 | # `vagrant box outdated`. This is not recommended.
20 | # config.vm.box_check_update = false
21 |
22 | # Create a forwarded port mapping which allows access to a specific port
23 | # within the machine from a port on the host machine. In the example below,
24 | # accessing "localhost:8080" will access port 80 on the guest machine.
25 | # config.vm.network "forwarded_port", guest: 80, host: 8080
26 |
27 | # Create a private network, which allows host-only access to the machine
28 | # using a specific IP.
29 | # config.vm.network "private_network", ip: "192.168.33.10"
30 |
31 | # Create a public network, which generally matched to bridged network.
32 | # Bridged networks make the machine appear as another physical device on
33 | # your network.
34 | # config.vm.network "public_network"
35 |
36 | # Share an additional folder to the guest VM. The first argument is
37 | # the path on the host to the actual folder. The second argument is
38 | # the path on the guest to mount the folder. And the optional third
39 | # argument is a set of non-required options.
40 | # config.vm.synced_folder "../data", "/vagrant_data"
41 |
42 | # Provider-specific configuration so you can fine-tune various
43 | # backing providers for Vagrant. These expose provider-specific options.
44 | # Example for VirtualBox:
45 | #
46 | # config.vm.provider "virtualbox" do |vb|
47 | # # Display the VirtualBox GUI when booting the machine
48 | # vb.gui = true
49 | #
50 | # # Customize the amount of memory on the VM:
51 | # vb.memory = "1024"
52 | # end
53 | #
54 | # View the documentation for the provider you are using for more
55 | # information on available options.
56 |
57 | # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
58 | # such as FTP and Heroku are also available. See the documentation at
59 | # https://docs.vagrantup.com/v2/push/atlas.html for more information.
60 | # config.push.define "atlas" do |push|
61 | # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
62 | # end
63 |
64 | # Enable provisioning with a shell script. Additional provisioners such as
65 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
66 | # documentation for more information about their specific syntax and use.
67 | # config.vm.provision "shell", inline: <<-SHELL
68 | # apt-get update
69 | # apt-get install -y apache2
70 | # SHELL
71 |
72 | config.vm.provider "virtualbox" do |vb|
73 | vb.memory = "1024"
74 | end
75 |
76 | config.vm.provision "shell", inline: <<-SHELL
77 | apt-get install -y aptitude &&
78 | aptitude update &&
79 | aptitude safe-upgrade -y &&
80 | aptitude dist-upgrade -y &&
81 | aptitude full-upgrade -y &&
82 | aptitude purge ~c -y &&
83 | aptitude clean
84 | export DEBIAN_FRONTEND="noninteractive"
85 | echo sudo debconf-set-selections <<< "mysql-server mysql-server/root_password password ''"
86 | echo sudo debconf-set-selections <<< "mysql-server mysql-server/root_password_again password ''"
87 | apt-get install -y gcc make libmysqlclient-dev mysql-server
88 | wget --quiet https://github.com/antirez/redis/archive/4.0-rc3.tar.gz
89 | tar xzf 4.0-rc3.tar.gz
90 | rm 4.0-rc3.tar.gz
91 | cd redis-4.0-rc3
92 | make
93 | sudo make install
94 | cd utils
95 | sudo ./install_server.sh
96 | cd ../..
97 | rm -Rf redis-4.0-rc3
98 | cd /vagrant/src/scache
99 | make clean
100 | make
101 | echo "create database redisdb;" | mysql -u root
102 | echo "create user redisuser@'%' identified by 'redispassword';" | mysql -u root
103 | echo "grant all privileges on redisdb.* to redisuser;" | mysql -u root
104 | echo "create table redisdb.customer (id integer, Nom varchar(255), Prenom varchar(255), DateDeNaissance datetime);" | mysql -u root
105 | echo 'insert into redisdb.customer values (1,"Cerbelle","François","2017-05-26");' | mysql -u root
106 | echo 'insert into redisdb.customer values (2,"Carbonnel","Georges","1970-01-01");' | mysql -u root
107 | echo 'insert into redisdb.customer values (3,"Sanfilippo","Salvatore","1970-01-01");' | mysql -u root
108 | sudo /etc/init.d/redis_6379 start
109 | redis-cli module load /vagrant/src/scache/scache.so
110 | redis-cli scache.list
111 | redis-cli scache.create cache1 20 127.0.0.1 3306 redisdb redisuser redispassword
112 | redis-cli scache.create cache2 10 localhost 3306 redisdb redisuser redispassword
113 | redis-cli scache.create cache3 5 nodeX.vm 3306 redisdb redisuser redispassword
114 | redis-cli scache.create cache4 5 localhost 3306 redisdb redisuser redispassword
115 | redis-cli scache.list
116 | redis-cli scache.info cache2
117 | redis-cli scache.delete cache2
118 | redis-cli scache.list
119 | redis-cli scache.delete nonexisting
120 | redis-cli scache.delete cache3
121 | redis-cli scache.delete nonexisting
122 | time redis-cli scache.getvalue cache1 'select * from customer'
123 | time redis-cli scache.getvalue cache1 'select * from customer'
124 | SHELL
125 | end
126 |
--------------------------------------------------------------------------------
/src/redismodule.h:
--------------------------------------------------------------------------------
1 | #ifndef REDISMODULE_H
2 | #define REDISMODULE_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | /* ---------------- Defines common between core and modules --------------- */
9 |
10 | /* Error status return values. */
11 | #define REDISMODULE_OK 0
12 | #define REDISMODULE_ERR 1
13 |
14 | /* API versions. */
15 | #define REDISMODULE_APIVER_1 1
16 |
17 | /* Version of the RedisModuleTypeMethods structure. Once the RedisModuleTypeMethods
18 | * structure is changed, this version number needs to be changed synchronistically. */
19 | #define REDISMODULE_TYPE_METHOD_VERSION 3
20 |
21 | /* API flags and constants */
22 | #define REDISMODULE_READ (1<<0)
23 | #define REDISMODULE_WRITE (1<<1)
24 |
25 | /* RedisModule_OpenKey extra flags for the 'mode' argument.
26 | * Avoid touching the LRU/LFU of the key when opened. */
27 | #define REDISMODULE_OPEN_KEY_NOTOUCH (1<<16)
28 |
29 | #define REDISMODULE_LIST_HEAD 0
30 | #define REDISMODULE_LIST_TAIL 1
31 |
32 | /* Key types. */
33 | #define REDISMODULE_KEYTYPE_EMPTY 0
34 | #define REDISMODULE_KEYTYPE_STRING 1
35 | #define REDISMODULE_KEYTYPE_LIST 2
36 | #define REDISMODULE_KEYTYPE_HASH 3
37 | #define REDISMODULE_KEYTYPE_SET 4
38 | #define REDISMODULE_KEYTYPE_ZSET 5
39 | #define REDISMODULE_KEYTYPE_MODULE 6
40 | #define REDISMODULE_KEYTYPE_STREAM 7
41 |
42 | /* Reply types. */
43 | #define REDISMODULE_REPLY_UNKNOWN -1
44 | #define REDISMODULE_REPLY_STRING 0
45 | #define REDISMODULE_REPLY_ERROR 1
46 | #define REDISMODULE_REPLY_INTEGER 2
47 | #define REDISMODULE_REPLY_ARRAY 3
48 | #define REDISMODULE_REPLY_NULL 4
49 |
50 | /* Postponed array length. */
51 | #define REDISMODULE_POSTPONED_ARRAY_LEN -1
52 |
53 | /* Expire */
54 | #define REDISMODULE_NO_EXPIRE -1
55 |
56 | /* Sorted set API flags. */
57 | #define REDISMODULE_ZADD_XX (1<<0)
58 | #define REDISMODULE_ZADD_NX (1<<1)
59 | #define REDISMODULE_ZADD_ADDED (1<<2)
60 | #define REDISMODULE_ZADD_UPDATED (1<<3)
61 | #define REDISMODULE_ZADD_NOP (1<<4)
62 | #define REDISMODULE_ZADD_GT (1<<5)
63 | #define REDISMODULE_ZADD_LT (1<<6)
64 |
65 | /* Hash API flags. */
66 | #define REDISMODULE_HASH_NONE 0
67 | #define REDISMODULE_HASH_NX (1<<0)
68 | #define REDISMODULE_HASH_XX (1<<1)
69 | #define REDISMODULE_HASH_CFIELDS (1<<2)
70 | #define REDISMODULE_HASH_EXISTS (1<<3)
71 |
72 | /* StreamID type. */
73 | typedef struct RedisModuleStreamID {
74 | uint64_t ms;
75 | uint64_t seq;
76 | } RedisModuleStreamID;
77 |
78 | /* StreamAdd() flags. */
79 | #define REDISMODULE_STREAM_ADD_AUTOID (1<<0)
80 | /* StreamIteratorStart() flags. */
81 | #define REDISMODULE_STREAM_ITERATOR_EXCLUSIVE (1<<0)
82 | #define REDISMODULE_STREAM_ITERATOR_REVERSE (1<<1)
83 | /* StreamIteratorTrim*() flags. */
84 | #define REDISMODULE_STREAM_TRIM_APPROX (1<<0)
85 |
86 | /* Context Flags: Info about the current context returned by
87 | * RM_GetContextFlags(). */
88 |
89 | /* The command is running in the context of a Lua script */
90 | #define REDISMODULE_CTX_FLAGS_LUA (1<<0)
91 | /* The command is running inside a Redis transaction */
92 | #define REDISMODULE_CTX_FLAGS_MULTI (1<<1)
93 | /* The instance is a master */
94 | #define REDISMODULE_CTX_FLAGS_MASTER (1<<2)
95 | /* The instance is a slave */
96 | #define REDISMODULE_CTX_FLAGS_SLAVE (1<<3)
97 | /* The instance is read-only (usually meaning it's a slave as well) */
98 | #define REDISMODULE_CTX_FLAGS_READONLY (1<<4)
99 | /* The instance is running in cluster mode */
100 | #define REDISMODULE_CTX_FLAGS_CLUSTER (1<<5)
101 | /* The instance has AOF enabled */
102 | #define REDISMODULE_CTX_FLAGS_AOF (1<<6)
103 | /* The instance has RDB enabled */
104 | #define REDISMODULE_CTX_FLAGS_RDB (1<<7)
105 | /* The instance has Maxmemory set */
106 | #define REDISMODULE_CTX_FLAGS_MAXMEMORY (1<<8)
107 | /* Maxmemory is set and has an eviction policy that may delete keys */
108 | #define REDISMODULE_CTX_FLAGS_EVICT (1<<9)
109 | /* Redis is out of memory according to the maxmemory flag. */
110 | #define REDISMODULE_CTX_FLAGS_OOM (1<<10)
111 | /* Less than 25% of memory available according to maxmemory. */
112 | #define REDISMODULE_CTX_FLAGS_OOM_WARNING (1<<11)
113 | /* The command was sent over the replication link. */
114 | #define REDISMODULE_CTX_FLAGS_REPLICATED (1<<12)
115 | /* Redis is currently loading either from AOF or RDB. */
116 | #define REDISMODULE_CTX_FLAGS_LOADING (1<<13)
117 | /* The replica has no link with its master, note that
118 | * there is the inverse flag as well:
119 | *
120 | * REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE
121 | *
122 | * The two flags are exclusive, one or the other can be set. */
123 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE (1<<14)
124 | /* The replica is trying to connect with the master.
125 | * (REPL_STATE_CONNECT and REPL_STATE_CONNECTING states) */
126 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING (1<<15)
127 | /* THe replica is receiving an RDB file from its master. */
128 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING (1<<16)
129 | /* The replica is online, receiving updates from its master. */
130 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE (1<<17)
131 | /* There is currently some background process active. */
132 | #define REDISMODULE_CTX_FLAGS_ACTIVE_CHILD (1<<18)
133 | /* The next EXEC will fail due to dirty CAS (touched keys). */
134 | #define REDISMODULE_CTX_FLAGS_MULTI_DIRTY (1<<19)
135 | /* Redis is currently running inside background child process. */
136 | #define REDISMODULE_CTX_FLAGS_IS_CHILD (1<<20)
137 | /* The current client does not allow blocking, either called from
138 | * within multi, lua, or from another module using RM_Call */
139 | #define REDISMODULE_CTX_FLAGS_DENY_BLOCKING (1<<21)
140 |
141 | /* Next context flag, must be updated when adding new flags above!
142 | This flag should not be used directly by the module.
143 | * Use RedisModule_GetContextFlagsAll instead. */
144 | #define _REDISMODULE_CTX_FLAGS_NEXT (1<<22)
145 |
146 | /* Keyspace changes notification classes. Every class is associated with a
147 | * character for configuration purposes.
148 | * NOTE: These have to be in sync with NOTIFY_* in server.h */
149 | #define REDISMODULE_NOTIFY_KEYSPACE (1<<0) /* K */
150 | #define REDISMODULE_NOTIFY_KEYEVENT (1<<1) /* E */
151 | #define REDISMODULE_NOTIFY_GENERIC (1<<2) /* g */
152 | #define REDISMODULE_NOTIFY_STRING (1<<3) /* $ */
153 | #define REDISMODULE_NOTIFY_LIST (1<<4) /* l */
154 | #define REDISMODULE_NOTIFY_SET (1<<5) /* s */
155 | #define REDISMODULE_NOTIFY_HASH (1<<6) /* h */
156 | #define REDISMODULE_NOTIFY_ZSET (1<<7) /* z */
157 | #define REDISMODULE_NOTIFY_EXPIRED (1<<8) /* x */
158 | #define REDISMODULE_NOTIFY_EVICTED (1<<9) /* e */
159 | #define REDISMODULE_NOTIFY_STREAM (1<<10) /* t */
160 | #define REDISMODULE_NOTIFY_KEY_MISS (1<<11) /* m (Note: This one is excluded from REDISMODULE_NOTIFY_ALL on purpose) */
161 | #define REDISMODULE_NOTIFY_LOADED (1<<12) /* module only key space notification, indicate a key loaded from rdb */
162 |
163 | /* Next notification flag, must be updated when adding new flags above!
164 | This flag should not be used directly by the module.
165 | * Use RedisModule_GetKeyspaceNotificationFlagsAll instead. */
166 | #define _REDISMODULE_NOTIFY_NEXT (1<<13)
167 |
168 | #define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED | REDISMODULE_NOTIFY_STREAM) /* A */
169 |
170 | /* A special pointer that we can use between the core and the module to signal
171 | * field deletion, and that is impossible to be a valid pointer. */
172 | #define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1)
173 |
174 | /* Error messages. */
175 | #define REDISMODULE_ERRORMSG_WRONGTYPE "WRONGTYPE Operation against a key holding the wrong kind of value"
176 |
177 | #define REDISMODULE_POSITIVE_INFINITE (1.0/0.0)
178 | #define REDISMODULE_NEGATIVE_INFINITE (-1.0/0.0)
179 |
180 | /* Cluster API defines. */
181 | #define REDISMODULE_NODE_ID_LEN 40
182 | #define REDISMODULE_NODE_MYSELF (1<<0)
183 | #define REDISMODULE_NODE_MASTER (1<<1)
184 | #define REDISMODULE_NODE_SLAVE (1<<2)
185 | #define REDISMODULE_NODE_PFAIL (1<<3)
186 | #define REDISMODULE_NODE_FAIL (1<<4)
187 | #define REDISMODULE_NODE_NOFAILOVER (1<<5)
188 |
189 | #define REDISMODULE_CLUSTER_FLAG_NONE 0
190 | #define REDISMODULE_CLUSTER_FLAG_NO_FAILOVER (1<<1)
191 | #define REDISMODULE_CLUSTER_FLAG_NO_REDIRECTION (1<<2)
192 |
193 | #define REDISMODULE_NOT_USED(V) ((void) V)
194 |
195 | /* Bit flags for aux_save_triggers and the aux_load and aux_save callbacks */
196 | #define REDISMODULE_AUX_BEFORE_RDB (1<<0)
197 | #define REDISMODULE_AUX_AFTER_RDB (1<<1)
198 |
199 | /* This type represents a timer handle, and is returned when a timer is
200 | * registered and used in order to invalidate a timer. It's just a 64 bit
201 | * number, because this is how each timer is represented inside the radix tree
202 | * of timers that are going to expire, sorted by expire time. */
203 | typedef uint64_t RedisModuleTimerID;
204 |
205 | /* CommandFilter Flags */
206 |
207 | /* Do filter RedisModule_Call() commands initiated by module itself. */
208 | #define REDISMODULE_CMDFILTER_NOSELF (1<<0)
209 |
210 | /* Declare that the module can handle errors with RedisModule_SetModuleOptions. */
211 | #define REDISMODULE_OPTIONS_HANDLE_IO_ERRORS (1<<0)
212 | /* When set, Redis will not call RedisModule_SignalModifiedKey(), implicitly in
213 | * RedisModule_CloseKey, and the module needs to do that when manually when keys
214 | * are modified from the user's sperspective, to invalidate WATCH. */
215 | #define REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED (1<<1)
216 |
217 | /* Server events definitions.
218 | * Those flags should not be used directly by the module, instead
219 | * the module should use RedisModuleEvent_* variables */
220 | #define REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED 0
221 | #define REDISMODULE_EVENT_PERSISTENCE 1
222 | #define REDISMODULE_EVENT_FLUSHDB 2
223 | #define REDISMODULE_EVENT_LOADING 3
224 | #define REDISMODULE_EVENT_CLIENT_CHANGE 4
225 | #define REDISMODULE_EVENT_SHUTDOWN 5
226 | #define REDISMODULE_EVENT_REPLICA_CHANGE 6
227 | #define REDISMODULE_EVENT_MASTER_LINK_CHANGE 7
228 | #define REDISMODULE_EVENT_CRON_LOOP 8
229 | #define REDISMODULE_EVENT_MODULE_CHANGE 9
230 | #define REDISMODULE_EVENT_LOADING_PROGRESS 10
231 | #define REDISMODULE_EVENT_SWAPDB 11
232 | #define REDISMODULE_EVENT_REPL_BACKUP 12
233 | #define REDISMODULE_EVENT_FORK_CHILD 13
234 | #define _REDISMODULE_EVENT_NEXT 14 /* Next event flag, should be updated if a new event added. */
235 |
236 | typedef struct RedisModuleEvent {
237 | uint64_t id; /* REDISMODULE_EVENT_... defines. */
238 | uint64_t dataver; /* Version of the structure we pass as 'data'. */
239 | } RedisModuleEvent;
240 |
241 | struct RedisModuleCtx;
242 | struct RedisModuleDefragCtx;
243 | typedef void (*RedisModuleEventCallback)(struct RedisModuleCtx *ctx, RedisModuleEvent eid, uint64_t subevent, void *data);
244 |
245 | static const RedisModuleEvent
246 | RedisModuleEvent_ReplicationRoleChanged = {
247 | REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED,
248 | 1
249 | },
250 | RedisModuleEvent_Persistence = {
251 | REDISMODULE_EVENT_PERSISTENCE,
252 | 1
253 | },
254 | RedisModuleEvent_FlushDB = {
255 | REDISMODULE_EVENT_FLUSHDB,
256 | 1
257 | },
258 | RedisModuleEvent_Loading = {
259 | REDISMODULE_EVENT_LOADING,
260 | 1
261 | },
262 | RedisModuleEvent_ClientChange = {
263 | REDISMODULE_EVENT_CLIENT_CHANGE,
264 | 1
265 | },
266 | RedisModuleEvent_Shutdown = {
267 | REDISMODULE_EVENT_SHUTDOWN,
268 | 1
269 | },
270 | RedisModuleEvent_ReplicaChange = {
271 | REDISMODULE_EVENT_REPLICA_CHANGE,
272 | 1
273 | },
274 | RedisModuleEvent_CronLoop = {
275 | REDISMODULE_EVENT_CRON_LOOP,
276 | 1
277 | },
278 | RedisModuleEvent_MasterLinkChange = {
279 | REDISMODULE_EVENT_MASTER_LINK_CHANGE,
280 | 1
281 | },
282 | RedisModuleEvent_ModuleChange = {
283 | REDISMODULE_EVENT_MODULE_CHANGE,
284 | 1
285 | },
286 | RedisModuleEvent_LoadingProgress = {
287 | REDISMODULE_EVENT_LOADING_PROGRESS,
288 | 1
289 | },
290 | RedisModuleEvent_SwapDB = {
291 | REDISMODULE_EVENT_SWAPDB,
292 | 1
293 | },
294 | RedisModuleEvent_ReplBackup = {
295 | REDISMODULE_EVENT_REPL_BACKUP,
296 | 1
297 | },
298 | RedisModuleEvent_ForkChild = {
299 | REDISMODULE_EVENT_FORK_CHILD,
300 | 1
301 | };
302 |
303 | /* Those are values that are used for the 'subevent' callback argument. */
304 | #define REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START 0
305 | #define REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START 1
306 | #define REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START 2
307 | #define REDISMODULE_SUBEVENT_PERSISTENCE_ENDED 3
308 | #define REDISMODULE_SUBEVENT_PERSISTENCE_FAILED 4
309 | #define _REDISMODULE_SUBEVENT_PERSISTENCE_NEXT 5
310 |
311 | #define REDISMODULE_SUBEVENT_LOADING_RDB_START 0
312 | #define REDISMODULE_SUBEVENT_LOADING_AOF_START 1
313 | #define REDISMODULE_SUBEVENT_LOADING_REPL_START 2
314 | #define REDISMODULE_SUBEVENT_LOADING_ENDED 3
315 | #define REDISMODULE_SUBEVENT_LOADING_FAILED 4
316 | #define _REDISMODULE_SUBEVENT_LOADING_NEXT 5
317 |
318 | #define REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED 0
319 | #define REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED 1
320 | #define _REDISMODULE_SUBEVENT_CLIENT_CHANGE_NEXT 2
321 |
322 | #define REDISMODULE_SUBEVENT_MASTER_LINK_UP 0
323 | #define REDISMODULE_SUBEVENT_MASTER_LINK_DOWN 1
324 | #define _REDISMODULE_SUBEVENT_MASTER_NEXT 2
325 |
326 | #define REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE 0
327 | #define REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE 1
328 | #define _REDISMODULE_SUBEVENT_REPLICA_CHANGE_NEXT 2
329 |
330 | #define REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER 0
331 | #define REDISMODULE_EVENT_REPLROLECHANGED_NOW_REPLICA 1
332 | #define _REDISMODULE_EVENT_REPLROLECHANGED_NEXT 2
333 |
334 | #define REDISMODULE_SUBEVENT_FLUSHDB_START 0
335 | #define REDISMODULE_SUBEVENT_FLUSHDB_END 1
336 | #define _REDISMODULE_SUBEVENT_FLUSHDB_NEXT 2
337 |
338 | #define REDISMODULE_SUBEVENT_MODULE_LOADED 0
339 | #define REDISMODULE_SUBEVENT_MODULE_UNLOADED 1
340 | #define _REDISMODULE_SUBEVENT_MODULE_NEXT 2
341 |
342 | #define REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB 0
343 | #define REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF 1
344 | #define _REDISMODULE_SUBEVENT_LOADING_PROGRESS_NEXT 2
345 |
346 | #define REDISMODULE_SUBEVENT_REPL_BACKUP_CREATE 0
347 | #define REDISMODULE_SUBEVENT_REPL_BACKUP_RESTORE 1
348 | #define REDISMODULE_SUBEVENT_REPL_BACKUP_DISCARD 2
349 | #define _REDISMODULE_SUBEVENT_REPL_BACKUP_NEXT 3
350 |
351 | #define REDISMODULE_SUBEVENT_FORK_CHILD_BORN 0
352 | #define REDISMODULE_SUBEVENT_FORK_CHILD_DIED 1
353 | #define _REDISMODULE_SUBEVENT_FORK_CHILD_NEXT 2
354 |
355 | #define _REDISMODULE_SUBEVENT_SHUTDOWN_NEXT 0
356 | #define _REDISMODULE_SUBEVENT_CRON_LOOP_NEXT 0
357 | #define _REDISMODULE_SUBEVENT_SWAPDB_NEXT 0
358 |
359 | /* RedisModuleClientInfo flags. */
360 | #define REDISMODULE_CLIENTINFO_FLAG_SSL (1<<0)
361 | #define REDISMODULE_CLIENTINFO_FLAG_PUBSUB (1<<1)
362 | #define REDISMODULE_CLIENTINFO_FLAG_BLOCKED (1<<2)
363 | #define REDISMODULE_CLIENTINFO_FLAG_TRACKING (1<<3)
364 | #define REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET (1<<4)
365 | #define REDISMODULE_CLIENTINFO_FLAG_MULTI (1<<5)
366 |
367 | /* Here we take all the structures that the module pass to the core
368 | * and the other way around. Notably the list here contains the structures
369 | * used by the hooks API RedisModule_RegisterToServerEvent().
370 | *
371 | * The structures always start with a 'version' field. This is useful
372 | * when we want to pass a reference to the structure to the core APIs,
373 | * for the APIs to fill the structure. In that case, the structure 'version'
374 | * field is initialized before passing it to the core, so that the core is
375 | * able to cast the pointer to the appropriate structure version. In this
376 | * way we obtain ABI compatibility.
377 | *
378 | * Here we'll list all the structure versions in case they evolve over time,
379 | * however using a define, we'll make sure to use the last version as the
380 | * public name for the module to use. */
381 |
382 | #define REDISMODULE_CLIENTINFO_VERSION 1
383 | typedef struct RedisModuleClientInfo {
384 | uint64_t version; /* Version of this structure for ABI compat. */
385 | uint64_t flags; /* REDISMODULE_CLIENTINFO_FLAG_* */
386 | uint64_t id; /* Client ID. */
387 | char addr[46]; /* IPv4 or IPv6 address. */
388 | uint16_t port; /* TCP port. */
389 | uint16_t db; /* Selected DB. */
390 | } RedisModuleClientInfoV1;
391 |
392 | #define RedisModuleClientInfo RedisModuleClientInfoV1
393 |
394 | #define REDISMODULE_REPLICATIONINFO_VERSION 1
395 | typedef struct RedisModuleReplicationInfo {
396 | uint64_t version; /* Not used since this structure is never passed
397 | from the module to the core right now. Here
398 | for future compatibility. */
399 | int master; /* true if master, false if replica */
400 | char *masterhost; /* master instance hostname for NOW_REPLICA */
401 | int masterport; /* master instance port for NOW_REPLICA */
402 | char *replid1; /* Main replication ID */
403 | char *replid2; /* Secondary replication ID */
404 | uint64_t repl1_offset; /* Main replication offset */
405 | uint64_t repl2_offset; /* Offset of replid2 validity */
406 | } RedisModuleReplicationInfoV1;
407 |
408 | #define RedisModuleReplicationInfo RedisModuleReplicationInfoV1
409 |
410 | #define REDISMODULE_FLUSHINFO_VERSION 1
411 | typedef struct RedisModuleFlushInfo {
412 | uint64_t version; /* Not used since this structure is never passed
413 | from the module to the core right now. Here
414 | for future compatibility. */
415 | int32_t sync; /* Synchronous or threaded flush?. */
416 | int32_t dbnum; /* Flushed database number, -1 for ALL. */
417 | } RedisModuleFlushInfoV1;
418 |
419 | #define RedisModuleFlushInfo RedisModuleFlushInfoV1
420 |
421 | #define REDISMODULE_MODULE_CHANGE_VERSION 1
422 | typedef struct RedisModuleModuleChange {
423 | uint64_t version; /* Not used since this structure is never passed
424 | from the module to the core right now. Here
425 | for future compatibility. */
426 | const char* module_name;/* Name of module loaded or unloaded. */
427 | int32_t module_version; /* Module version. */
428 | } RedisModuleModuleChangeV1;
429 |
430 | #define RedisModuleModuleChange RedisModuleModuleChangeV1
431 |
432 | #define REDISMODULE_CRON_LOOP_VERSION 1
433 | typedef struct RedisModuleCronLoopInfo {
434 | uint64_t version; /* Not used since this structure is never passed
435 | from the module to the core right now. Here
436 | for future compatibility. */
437 | int32_t hz; /* Approximate number of events per second. */
438 | } RedisModuleCronLoopV1;
439 |
440 | #define RedisModuleCronLoop RedisModuleCronLoopV1
441 |
442 | #define REDISMODULE_LOADING_PROGRESS_VERSION 1
443 | typedef struct RedisModuleLoadingProgressInfo {
444 | uint64_t version; /* Not used since this structure is never passed
445 | from the module to the core right now. Here
446 | for future compatibility. */
447 | int32_t hz; /* Approximate number of events per second. */
448 | int32_t progress; /* Approximate progress between 0 and 1024, or -1
449 | * if unknown. */
450 | } RedisModuleLoadingProgressV1;
451 |
452 | #define RedisModuleLoadingProgress RedisModuleLoadingProgressV1
453 |
454 | #define REDISMODULE_SWAPDBINFO_VERSION 1
455 | typedef struct RedisModuleSwapDbInfo {
456 | uint64_t version; /* Not used since this structure is never passed
457 | from the module to the core right now. Here
458 | for future compatibility. */
459 | int32_t dbnum_first; /* Swap Db first dbnum */
460 | int32_t dbnum_second; /* Swap Db second dbnum */
461 | } RedisModuleSwapDbInfoV1;
462 |
463 | #define RedisModuleSwapDbInfo RedisModuleSwapDbInfoV1
464 |
465 | /* ------------------------- End of common defines ------------------------ */
466 |
467 | #ifndef REDISMODULE_CORE
468 |
469 | typedef long long mstime_t;
470 |
471 | /* Macro definitions specific to individual compilers */
472 | #ifndef REDISMODULE_ATTR_UNUSED
473 | # ifdef __GNUC__
474 | # define REDISMODULE_ATTR_UNUSED __attribute__((unused))
475 | # else
476 | # define REDISMODULE_ATTR_UNUSED
477 | # endif
478 | #endif
479 |
480 | #ifndef REDISMODULE_ATTR_PRINTF
481 | # ifdef __GNUC__
482 | # define REDISMODULE_ATTR_PRINTF(idx,cnt) __attribute__((format(printf,idx,cnt)))
483 | # else
484 | # define REDISMODULE_ATTR_PRINTF(idx,cnt)
485 | # endif
486 | #endif
487 |
488 | #ifndef REDISMODULE_ATTR_COMMON
489 | # if defined(__GNUC__) && !defined(__clang__)
490 | # define REDISMODULE_ATTR_COMMON __attribute__((__common__))
491 | # else
492 | # define REDISMODULE_ATTR_COMMON
493 | # endif
494 | #endif
495 |
496 | /* Incomplete structures for compiler checks but opaque access. */
497 | typedef struct RedisModuleCtx RedisModuleCtx;
498 | typedef struct RedisModuleKey RedisModuleKey;
499 | typedef struct RedisModuleString RedisModuleString;
500 | typedef struct RedisModuleCallReply RedisModuleCallReply;
501 | typedef struct RedisModuleIO RedisModuleIO;
502 | typedef struct RedisModuleType RedisModuleType;
503 | typedef struct RedisModuleDigest RedisModuleDigest;
504 | typedef struct RedisModuleBlockedClient RedisModuleBlockedClient;
505 | typedef struct RedisModuleClusterInfo RedisModuleClusterInfo;
506 | typedef struct RedisModuleDict RedisModuleDict;
507 | typedef struct RedisModuleDictIter RedisModuleDictIter;
508 | typedef struct RedisModuleCommandFilterCtx RedisModuleCommandFilterCtx;
509 | typedef struct RedisModuleCommandFilter RedisModuleCommandFilter;
510 | typedef struct RedisModuleInfoCtx RedisModuleInfoCtx;
511 | typedef struct RedisModuleServerInfoData RedisModuleServerInfoData;
512 | typedef struct RedisModuleScanCursor RedisModuleScanCursor;
513 | typedef struct RedisModuleDefragCtx RedisModuleDefragCtx;
514 | typedef struct RedisModuleUser RedisModuleUser;
515 |
516 | typedef int (*RedisModuleCmdFunc)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
517 | typedef void (*RedisModuleDisconnectFunc)(RedisModuleCtx *ctx, RedisModuleBlockedClient *bc);
518 | typedef int (*RedisModuleNotificationFunc)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key);
519 | typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver);
520 | typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value);
521 | typedef int (*RedisModuleTypeAuxLoadFunc)(RedisModuleIO *rdb, int encver, int when);
522 | typedef void (*RedisModuleTypeAuxSaveFunc)(RedisModuleIO *rdb, int when);
523 | typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value);
524 | typedef size_t (*RedisModuleTypeMemUsageFunc)(const void *value);
525 | typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value);
526 | typedef void (*RedisModuleTypeFreeFunc)(void *value);
527 | typedef size_t (*RedisModuleTypeFreeEffortFunc)(RedisModuleString *key, const void *value);
528 | typedef void (*RedisModuleTypeUnlinkFunc)(RedisModuleString *key, const void *value);
529 | typedef void *(*RedisModuleTypeCopyFunc)(RedisModuleString *fromkey, RedisModuleString *tokey, const void *value);
530 | typedef int (*RedisModuleTypeDefragFunc)(RedisModuleDefragCtx *ctx, RedisModuleString *key, void **value);
531 | typedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len);
532 | typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data);
533 | typedef void (*RedisModuleCommandFilterFunc) (RedisModuleCommandFilterCtx *filter);
534 | typedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data);
535 | typedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report);
536 | typedef void (*RedisModuleScanCB)(RedisModuleCtx *ctx, RedisModuleString *keyname, RedisModuleKey *key, void *privdata);
537 | typedef void (*RedisModuleScanKeyCB)(RedisModuleKey *key, RedisModuleString *field, RedisModuleString *value, void *privdata);
538 | typedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata);
539 | typedef int (*RedisModuleDefragFunc)(RedisModuleDefragCtx *ctx);
540 |
541 | typedef struct RedisModuleTypeMethods {
542 | uint64_t version;
543 | RedisModuleTypeLoadFunc rdb_load;
544 | RedisModuleTypeSaveFunc rdb_save;
545 | RedisModuleTypeRewriteFunc aof_rewrite;
546 | RedisModuleTypeMemUsageFunc mem_usage;
547 | RedisModuleTypeDigestFunc digest;
548 | RedisModuleTypeFreeFunc free;
549 | RedisModuleTypeAuxLoadFunc aux_load;
550 | RedisModuleTypeAuxSaveFunc aux_save;
551 | int aux_save_triggers;
552 | RedisModuleTypeFreeEffortFunc free_effort;
553 | RedisModuleTypeUnlinkFunc unlink;
554 | RedisModuleTypeCopyFunc copy;
555 | RedisModuleTypeDefragFunc defrag;
556 | } RedisModuleTypeMethods;
557 |
558 | #define REDISMODULE_GET_API(name) \
559 | RedisModule_GetApi("RedisModule_" #name, ((void **)&RedisModule_ ## name))
560 |
561 | /* Default API declaration prefix (not 'extern' for backwards compatibility) */
562 | #ifndef REDISMODULE_API
563 | #define REDISMODULE_API
564 | #endif
565 |
566 | /* Default API declaration suffix (compiler attributes) */
567 | #ifndef REDISMODULE_ATTR
568 | #define REDISMODULE_ATTR REDISMODULE_ATTR_COMMON
569 | #endif
570 |
571 | REDISMODULE_API void * (*RedisModule_Alloc)(size_t bytes) REDISMODULE_ATTR;
572 | REDISMODULE_API void * (*RedisModule_Realloc)(void *ptr, size_t bytes) REDISMODULE_ATTR;
573 | REDISMODULE_API void (*RedisModule_Free)(void *ptr) REDISMODULE_ATTR;
574 | REDISMODULE_API void * (*RedisModule_Calloc)(size_t nmemb, size_t size) REDISMODULE_ATTR;
575 | REDISMODULE_API char * (*RedisModule_Strdup)(const char *str) REDISMODULE_ATTR;
576 | REDISMODULE_API int (*RedisModule_GetApi)(const char *, void *) REDISMODULE_ATTR;
577 | REDISMODULE_API int (*RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) REDISMODULE_ATTR;
578 | REDISMODULE_API void (*RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver) REDISMODULE_ATTR;
579 | REDISMODULE_API int (*RedisModule_IsModuleNameBusy)(const char *name) REDISMODULE_ATTR;
580 | REDISMODULE_API int (*RedisModule_WrongArity)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
581 | REDISMODULE_API int (*RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll) REDISMODULE_ATTR;
582 | REDISMODULE_API int (*RedisModule_GetSelectedDb)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
583 | REDISMODULE_API int (*RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid) REDISMODULE_ATTR;
584 | REDISMODULE_API void * (*RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode) REDISMODULE_ATTR;
585 | REDISMODULE_API void (*RedisModule_CloseKey)(RedisModuleKey *kp) REDISMODULE_ATTR;
586 | REDISMODULE_API int (*RedisModule_KeyType)(RedisModuleKey *kp) REDISMODULE_ATTR;
587 | REDISMODULE_API size_t (*RedisModule_ValueLength)(RedisModuleKey *kp) REDISMODULE_ATTR;
588 | REDISMODULE_API int (*RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele) REDISMODULE_ATTR;
589 | REDISMODULE_API RedisModuleString * (*RedisModule_ListPop)(RedisModuleKey *key, int where) REDISMODULE_ATTR;
590 | REDISMODULE_API RedisModuleCallReply * (*RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) REDISMODULE_ATTR;
591 | REDISMODULE_API const char * (*RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len) REDISMODULE_ATTR;
592 | REDISMODULE_API void (*RedisModule_FreeCallReply)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
593 | REDISMODULE_API int (*RedisModule_CallReplyType)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
594 | REDISMODULE_API long long (*RedisModule_CallReplyInteger)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
595 | REDISMODULE_API size_t (*RedisModule_CallReplyLength)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
596 | REDISMODULE_API RedisModuleCallReply * (*RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx) REDISMODULE_ATTR;
597 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len) REDISMODULE_ATTR;
598 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll) REDISMODULE_ATTR;
599 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromDouble)(RedisModuleCtx *ctx, double d) REDISMODULE_ATTR;
600 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromLongDouble)(RedisModuleCtx *ctx, long double ld, int humanfriendly) REDISMODULE_ATTR;
601 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str) REDISMODULE_ATTR;
602 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromStreamID)(RedisModuleCtx *ctx, const RedisModuleStreamID *id) REDISMODULE_ATTR;
603 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...) REDISMODULE_ATTR_PRINTF(2,3) REDISMODULE_ATTR;
604 | REDISMODULE_API void (*RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
605 | REDISMODULE_API const char * (*RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len) REDISMODULE_ATTR;
606 | REDISMODULE_API int (*RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err) REDISMODULE_ATTR;
607 | REDISMODULE_API int (*RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg) REDISMODULE_ATTR;
608 | REDISMODULE_API int (*RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR;
609 | REDISMODULE_API int (*RedisModule_ReplyWithNullArray)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
610 | REDISMODULE_API int (*RedisModule_ReplyWithEmptyArray)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
611 | REDISMODULE_API void (*RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR;
612 | REDISMODULE_API int (*RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len) REDISMODULE_ATTR;
613 | REDISMODULE_API int (*RedisModule_ReplyWithCString)(RedisModuleCtx *ctx, const char *buf) REDISMODULE_ATTR;
614 | REDISMODULE_API int (*RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
615 | REDISMODULE_API int (*RedisModule_ReplyWithEmptyString)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
616 | REDISMODULE_API int (*RedisModule_ReplyWithVerbatimString)(RedisModuleCtx *ctx, const char *buf, size_t len) REDISMODULE_ATTR;
617 | REDISMODULE_API int (*RedisModule_ReplyWithNull)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
618 | REDISMODULE_API int (*RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d) REDISMODULE_ATTR;
619 | REDISMODULE_API int (*RedisModule_ReplyWithLongDouble)(RedisModuleCtx *ctx, long double d) REDISMODULE_ATTR;
620 | REDISMODULE_API int (*RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply) REDISMODULE_ATTR;
621 | REDISMODULE_API int (*RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll) REDISMODULE_ATTR;
622 | REDISMODULE_API int (*RedisModule_StringToDouble)(const RedisModuleString *str, double *d) REDISMODULE_ATTR;
623 | REDISMODULE_API int (*RedisModule_StringToLongDouble)(const RedisModuleString *str, long double *d) REDISMODULE_ATTR;
624 | REDISMODULE_API int (*RedisModule_StringToStreamID)(const RedisModuleString *str, RedisModuleStreamID *id) REDISMODULE_ATTR;
625 | REDISMODULE_API void (*RedisModule_AutoMemory)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
626 | REDISMODULE_API int (*RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) REDISMODULE_ATTR;
627 | REDISMODULE_API int (*RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
628 | REDISMODULE_API const char * (*RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len) REDISMODULE_ATTR;
629 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
630 | REDISMODULE_API int (*RedisModule_DeleteKey)(RedisModuleKey *key) REDISMODULE_ATTR;
631 | REDISMODULE_API int (*RedisModule_UnlinkKey)(RedisModuleKey *key) REDISMODULE_ATTR;
632 | REDISMODULE_API int (*RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str) REDISMODULE_ATTR;
633 | REDISMODULE_API char * (*RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode) REDISMODULE_ATTR;
634 | REDISMODULE_API int (*RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen) REDISMODULE_ATTR;
635 | REDISMODULE_API mstime_t (*RedisModule_GetExpire)(RedisModuleKey *key) REDISMODULE_ATTR;
636 | REDISMODULE_API int (*RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire) REDISMODULE_ATTR;
637 | REDISMODULE_API void (*RedisModule_ResetDataset)(int restart_aof, int async) REDISMODULE_ATTR;
638 | REDISMODULE_API unsigned long long (*RedisModule_DbSize)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
639 | REDISMODULE_API RedisModuleString * (*RedisModule_RandomKey)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
640 | REDISMODULE_API int (*RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr) REDISMODULE_ATTR;
641 | REDISMODULE_API int (*RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore) REDISMODULE_ATTR;
642 | REDISMODULE_API int (*RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score) REDISMODULE_ATTR;
643 | REDISMODULE_API int (*RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted) REDISMODULE_ATTR;
644 | REDISMODULE_API void (*RedisModule_ZsetRangeStop)(RedisModuleKey *key) REDISMODULE_ATTR;
645 | REDISMODULE_API int (*RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex) REDISMODULE_ATTR;
646 | REDISMODULE_API int (*RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex) REDISMODULE_ATTR;
647 | REDISMODULE_API int (*RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) REDISMODULE_ATTR;
648 | REDISMODULE_API int (*RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) REDISMODULE_ATTR;
649 | REDISMODULE_API RedisModuleString * (*RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score) REDISMODULE_ATTR;
650 | REDISMODULE_API int (*RedisModule_ZsetRangeNext)(RedisModuleKey *key) REDISMODULE_ATTR;
651 | REDISMODULE_API int (*RedisModule_ZsetRangePrev)(RedisModuleKey *key) REDISMODULE_ATTR;
652 | REDISMODULE_API int (*RedisModule_ZsetRangeEndReached)(RedisModuleKey *key) REDISMODULE_ATTR;
653 | REDISMODULE_API int (*RedisModule_HashSet)(RedisModuleKey *key, int flags, ...) REDISMODULE_ATTR;
654 | REDISMODULE_API int (*RedisModule_HashGet)(RedisModuleKey *key, int flags, ...) REDISMODULE_ATTR;
655 | REDISMODULE_API int (*RedisModule_StreamAdd)(RedisModuleKey *key, int flags, RedisModuleStreamID *id, RedisModuleString **argv, int64_t numfields) REDISMODULE_ATTR;
656 | REDISMODULE_API int (*RedisModule_StreamDelete)(RedisModuleKey *key, RedisModuleStreamID *id) REDISMODULE_ATTR;
657 | REDISMODULE_API int (*RedisModule_StreamIteratorStart)(RedisModuleKey *key, int flags, RedisModuleStreamID *startid, RedisModuleStreamID *endid) REDISMODULE_ATTR;
658 | REDISMODULE_API int (*RedisModule_StreamIteratorStop)(RedisModuleKey *key) REDISMODULE_ATTR;
659 | REDISMODULE_API int (*RedisModule_StreamIteratorNextID)(RedisModuleKey *key, RedisModuleStreamID *id, long *numfields) REDISMODULE_ATTR;
660 | REDISMODULE_API int (*RedisModule_StreamIteratorNextField)(RedisModuleKey *key, RedisModuleString **field_ptr, RedisModuleString **value_ptr) REDISMODULE_ATTR;
661 | REDISMODULE_API int (*RedisModule_StreamIteratorDelete)(RedisModuleKey *key) REDISMODULE_ATTR;
662 | REDISMODULE_API long long (*RedisModule_StreamTrimByLength)(RedisModuleKey *key, int flags, long long length) REDISMODULE_ATTR;
663 | REDISMODULE_API long long (*RedisModule_StreamTrimByID)(RedisModuleKey *key, int flags, RedisModuleStreamID *id) REDISMODULE_ATTR;
664 | REDISMODULE_API int (*RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
665 | REDISMODULE_API void (*RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos) REDISMODULE_ATTR;
666 | REDISMODULE_API unsigned long long (*RedisModule_GetClientId)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
667 | REDISMODULE_API int (*RedisModule_GetClientInfoById)(void *ci, uint64_t id) REDISMODULE_ATTR;
668 | REDISMODULE_API int (*RedisModule_PublishMessage)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) REDISMODULE_ATTR;
669 | REDISMODULE_API int (*RedisModule_GetContextFlags)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
670 | REDISMODULE_API int (*RedisModule_AvoidReplicaTraffic)() REDISMODULE_ATTR;
671 | REDISMODULE_API void * (*RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes) REDISMODULE_ATTR;
672 | REDISMODULE_API RedisModuleType * (*RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods) REDISMODULE_ATTR;
673 | REDISMODULE_API int (*RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value) REDISMODULE_ATTR;
674 | REDISMODULE_API int (*RedisModule_ModuleTypeReplaceValue)(RedisModuleKey *key, RedisModuleType *mt, void *new_value, void **old_value) REDISMODULE_ATTR;
675 | REDISMODULE_API RedisModuleType * (*RedisModule_ModuleTypeGetType)(RedisModuleKey *key) REDISMODULE_ATTR;
676 | REDISMODULE_API void * (*RedisModule_ModuleTypeGetValue)(RedisModuleKey *key) REDISMODULE_ATTR;
677 | REDISMODULE_API int (*RedisModule_IsIOError)(RedisModuleIO *io) REDISMODULE_ATTR;
678 | REDISMODULE_API void (*RedisModule_SetModuleOptions)(RedisModuleCtx *ctx, int options) REDISMODULE_ATTR;
679 | REDISMODULE_API int (*RedisModule_SignalModifiedKey)(RedisModuleCtx *ctx, RedisModuleString *keyname) REDISMODULE_ATTR;
680 | REDISMODULE_API void (*RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value) REDISMODULE_ATTR;
681 | REDISMODULE_API uint64_t (*RedisModule_LoadUnsigned)(RedisModuleIO *io) REDISMODULE_ATTR;
682 | REDISMODULE_API void (*RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value) REDISMODULE_ATTR;
683 | REDISMODULE_API int64_t (*RedisModule_LoadSigned)(RedisModuleIO *io) REDISMODULE_ATTR;
684 | REDISMODULE_API void (*RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...) REDISMODULE_ATTR;
685 | REDISMODULE_API void (*RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s) REDISMODULE_ATTR;
686 | REDISMODULE_API void (*RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len) REDISMODULE_ATTR;
687 | REDISMODULE_API RedisModuleString * (*RedisModule_LoadString)(RedisModuleIO *io) REDISMODULE_ATTR;
688 | REDISMODULE_API char * (*RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr) REDISMODULE_ATTR;
689 | REDISMODULE_API void (*RedisModule_SaveDouble)(RedisModuleIO *io, double value) REDISMODULE_ATTR;
690 | REDISMODULE_API double (*RedisModule_LoadDouble)(RedisModuleIO *io) REDISMODULE_ATTR;
691 | REDISMODULE_API void (*RedisModule_SaveFloat)(RedisModuleIO *io, float value) REDISMODULE_ATTR;
692 | REDISMODULE_API float (*RedisModule_LoadFloat)(RedisModuleIO *io) REDISMODULE_ATTR;
693 | REDISMODULE_API void (*RedisModule_SaveLongDouble)(RedisModuleIO *io, long double value) REDISMODULE_ATTR;
694 | REDISMODULE_API long double (*RedisModule_LoadLongDouble)(RedisModuleIO *io) REDISMODULE_ATTR;
695 | REDISMODULE_API void * (*RedisModule_LoadDataTypeFromString)(const RedisModuleString *str, const RedisModuleType *mt) REDISMODULE_ATTR;
696 | REDISMODULE_API RedisModuleString * (*RedisModule_SaveDataTypeToString)(RedisModuleCtx *ctx, void *data, const RedisModuleType *mt) REDISMODULE_ATTR;
697 | REDISMODULE_API void (*RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...) REDISMODULE_ATTR REDISMODULE_ATTR_PRINTF(3,4);
698 | REDISMODULE_API void (*RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) REDISMODULE_ATTR REDISMODULE_ATTR_PRINTF(3,4);
699 | REDISMODULE_API void (*RedisModule__Assert)(const char *estr, const char *file, int line) REDISMODULE_ATTR;
700 | REDISMODULE_API void (*RedisModule_LatencyAddSample)(const char *event, mstime_t latency) REDISMODULE_ATTR;
701 | REDISMODULE_API int (*RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len) REDISMODULE_ATTR;
702 | REDISMODULE_API void (*RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
703 | REDISMODULE_API RedisModuleString * (*RedisModule_HoldString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
704 | REDISMODULE_API int (*RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b) REDISMODULE_ATTR;
705 | REDISMODULE_API RedisModuleCtx * (*RedisModule_GetContextFromIO)(RedisModuleIO *io) REDISMODULE_ATTR;
706 | REDISMODULE_API const RedisModuleString * (*RedisModule_GetKeyNameFromIO)(RedisModuleIO *io) REDISMODULE_ATTR;
707 | REDISMODULE_API const RedisModuleString * (*RedisModule_GetKeyNameFromModuleKey)(RedisModuleKey *key) REDISMODULE_ATTR;
708 | REDISMODULE_API long long (*RedisModule_Milliseconds)(void) REDISMODULE_ATTR;
709 | REDISMODULE_API void (*RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len) REDISMODULE_ATTR;
710 | REDISMODULE_API void (*RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele) REDISMODULE_ATTR;
711 | REDISMODULE_API void (*RedisModule_DigestEndSequence)(RedisModuleDigest *md) REDISMODULE_ATTR;
712 | REDISMODULE_API RedisModuleDict * (*RedisModule_CreateDict)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
713 | REDISMODULE_API void (*RedisModule_FreeDict)(RedisModuleCtx *ctx, RedisModuleDict *d) REDISMODULE_ATTR;
714 | REDISMODULE_API uint64_t (*RedisModule_DictSize)(RedisModuleDict *d) REDISMODULE_ATTR;
715 | REDISMODULE_API int (*RedisModule_DictSetC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr) REDISMODULE_ATTR;
716 | REDISMODULE_API int (*RedisModule_DictReplaceC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr) REDISMODULE_ATTR;
717 | REDISMODULE_API int (*RedisModule_DictSet)(RedisModuleDict *d, RedisModuleString *key, void *ptr) REDISMODULE_ATTR;
718 | REDISMODULE_API int (*RedisModule_DictReplace)(RedisModuleDict *d, RedisModuleString *key, void *ptr) REDISMODULE_ATTR;
719 | REDISMODULE_API void * (*RedisModule_DictGetC)(RedisModuleDict *d, void *key, size_t keylen, int *nokey) REDISMODULE_ATTR;
720 | REDISMODULE_API void * (*RedisModule_DictGet)(RedisModuleDict *d, RedisModuleString *key, int *nokey) REDISMODULE_ATTR;
721 | REDISMODULE_API int (*RedisModule_DictDelC)(RedisModuleDict *d, void *key, size_t keylen, void *oldval) REDISMODULE_ATTR;
722 | REDISMODULE_API int (*RedisModule_DictDel)(RedisModuleDict *d, RedisModuleString *key, void *oldval) REDISMODULE_ATTR;
723 | REDISMODULE_API RedisModuleDictIter * (*RedisModule_DictIteratorStartC)(RedisModuleDict *d, const char *op, void *key, size_t keylen) REDISMODULE_ATTR;
724 | REDISMODULE_API RedisModuleDictIter * (*RedisModule_DictIteratorStart)(RedisModuleDict *d, const char *op, RedisModuleString *key) REDISMODULE_ATTR;
725 | REDISMODULE_API void (*RedisModule_DictIteratorStop)(RedisModuleDictIter *di) REDISMODULE_ATTR;
726 | REDISMODULE_API int (*RedisModule_DictIteratorReseekC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) REDISMODULE_ATTR;
727 | REDISMODULE_API int (*RedisModule_DictIteratorReseek)(RedisModuleDictIter *di, const char *op, RedisModuleString *key) REDISMODULE_ATTR;
728 | REDISMODULE_API void * (*RedisModule_DictNextC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr) REDISMODULE_ATTR;
729 | REDISMODULE_API void * (*RedisModule_DictPrevC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr) REDISMODULE_ATTR;
730 | REDISMODULE_API RedisModuleString * (*RedisModule_DictNext)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) REDISMODULE_ATTR;
731 | REDISMODULE_API RedisModuleString * (*RedisModule_DictPrev)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) REDISMODULE_ATTR;
732 | REDISMODULE_API int (*RedisModule_DictCompareC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) REDISMODULE_ATTR;
733 | REDISMODULE_API int (*RedisModule_DictCompare)(RedisModuleDictIter *di, const char *op, RedisModuleString *key) REDISMODULE_ATTR;
734 | REDISMODULE_API int (*RedisModule_RegisterInfoFunc)(RedisModuleCtx *ctx, RedisModuleInfoFunc cb) REDISMODULE_ATTR;
735 | REDISMODULE_API int (*RedisModule_InfoAddSection)(RedisModuleInfoCtx *ctx, char *name) REDISMODULE_ATTR;
736 | REDISMODULE_API int (*RedisModule_InfoBeginDictField)(RedisModuleInfoCtx *ctx, char *name) REDISMODULE_ATTR;
737 | REDISMODULE_API int (*RedisModule_InfoEndDictField)(RedisModuleInfoCtx *ctx) REDISMODULE_ATTR;
738 | REDISMODULE_API int (*RedisModule_InfoAddFieldString)(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value) REDISMODULE_ATTR;
739 | REDISMODULE_API int (*RedisModule_InfoAddFieldCString)(RedisModuleInfoCtx *ctx, char *field, char *value) REDISMODULE_ATTR;
740 | REDISMODULE_API int (*RedisModule_InfoAddFieldDouble)(RedisModuleInfoCtx *ctx, char *field, double value) REDISMODULE_ATTR;
741 | REDISMODULE_API int (*RedisModule_InfoAddFieldLongLong)(RedisModuleInfoCtx *ctx, char *field, long long value) REDISMODULE_ATTR;
742 | REDISMODULE_API int (*RedisModule_InfoAddFieldULongLong)(RedisModuleInfoCtx *ctx, char *field, unsigned long long value) REDISMODULE_ATTR;
743 | REDISMODULE_API RedisModuleServerInfoData * (*RedisModule_GetServerInfo)(RedisModuleCtx *ctx, const char *section) REDISMODULE_ATTR;
744 | REDISMODULE_API void (*RedisModule_FreeServerInfo)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data) REDISMODULE_ATTR;
745 | REDISMODULE_API RedisModuleString * (*RedisModule_ServerInfoGetField)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data, const char* field) REDISMODULE_ATTR;
746 | REDISMODULE_API const char * (*RedisModule_ServerInfoGetFieldC)(RedisModuleServerInfoData *data, const char* field) REDISMODULE_ATTR;
747 | REDISMODULE_API long long (*RedisModule_ServerInfoGetFieldSigned)(RedisModuleServerInfoData *data, const char* field, int *out_err) REDISMODULE_ATTR;
748 | REDISMODULE_API unsigned long long (*RedisModule_ServerInfoGetFieldUnsigned)(RedisModuleServerInfoData *data, const char* field, int *out_err) REDISMODULE_ATTR;
749 | REDISMODULE_API double (*RedisModule_ServerInfoGetFieldDouble)(RedisModuleServerInfoData *data, const char* field, int *out_err) REDISMODULE_ATTR;
750 | REDISMODULE_API int (*RedisModule_SubscribeToServerEvent)(RedisModuleCtx *ctx, RedisModuleEvent event, RedisModuleEventCallback callback) REDISMODULE_ATTR;
751 | REDISMODULE_API int (*RedisModule_SetLRU)(RedisModuleKey *key, mstime_t lru_idle) REDISMODULE_ATTR;
752 | REDISMODULE_API int (*RedisModule_GetLRU)(RedisModuleKey *key, mstime_t *lru_idle) REDISMODULE_ATTR;
753 | REDISMODULE_API int (*RedisModule_SetLFU)(RedisModuleKey *key, long long lfu_freq) REDISMODULE_ATTR;
754 | REDISMODULE_API int (*RedisModule_GetLFU)(RedisModuleKey *key, long long *lfu_freq) REDISMODULE_ATTR;
755 | REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_BlockClientOnKeys)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata) REDISMODULE_ATTR;
756 | REDISMODULE_API void (*RedisModule_SignalKeyAsReady)(RedisModuleCtx *ctx, RedisModuleString *key) REDISMODULE_ATTR;
757 | REDISMODULE_API RedisModuleString * (*RedisModule_GetBlockedClientReadyKey)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
758 | REDISMODULE_API RedisModuleScanCursor * (*RedisModule_ScanCursorCreate)() REDISMODULE_ATTR;
759 | REDISMODULE_API void (*RedisModule_ScanCursorRestart)(RedisModuleScanCursor *cursor) REDISMODULE_ATTR;
760 | REDISMODULE_API void (*RedisModule_ScanCursorDestroy)(RedisModuleScanCursor *cursor) REDISMODULE_ATTR;
761 | REDISMODULE_API int (*RedisModule_Scan)(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata) REDISMODULE_ATTR;
762 | REDISMODULE_API int (*RedisModule_ScanKey)(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata) REDISMODULE_ATTR;
763 | REDISMODULE_API int (*RedisModule_GetContextFlagsAll)() REDISMODULE_ATTR;
764 | REDISMODULE_API int (*RedisModule_GetKeyspaceNotificationFlagsAll)() REDISMODULE_ATTR;
765 | REDISMODULE_API int (*RedisModule_IsSubEventSupported)(RedisModuleEvent event, uint64_t subevent) REDISMODULE_ATTR;
766 | REDISMODULE_API int (*RedisModule_GetServerVersion)() REDISMODULE_ATTR;
767 | REDISMODULE_API int (*RedisModule_GetTypeMethodVersion)() REDISMODULE_ATTR;
768 |
769 | /* Experimental APIs */
770 | #ifdef REDISMODULE_EXPERIMENTAL_API
771 | #define REDISMODULE_EXPERIMENTAL_API_VERSION 3
772 | REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms) REDISMODULE_ATTR;
773 | REDISMODULE_API int (*RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata) REDISMODULE_ATTR;
774 | REDISMODULE_API int (*RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
775 | REDISMODULE_API int (*RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
776 | REDISMODULE_API void * (*RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
777 | REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_GetBlockedClientHandle)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
778 | REDISMODULE_API int (*RedisModule_AbortBlock)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR;
779 | REDISMODULE_API int (*RedisModule_BlockedClientMeasureTimeStart)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR;
780 | REDISMODULE_API int (*RedisModule_BlockedClientMeasureTimeEnd)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR;
781 | REDISMODULE_API RedisModuleCtx * (*RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR;
782 | REDISMODULE_API RedisModuleCtx * (*RedisModule_GetDetachedThreadSafeContext)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
783 | REDISMODULE_API void (*RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
784 | REDISMODULE_API void (*RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
785 | REDISMODULE_API int (*RedisModule_ThreadSafeContextTryLock)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
786 | REDISMODULE_API void (*RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
787 | REDISMODULE_API int (*RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb) REDISMODULE_ATTR;
788 | REDISMODULE_API int (*RedisModule_NotifyKeyspaceEvent)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) REDISMODULE_ATTR;
789 | REDISMODULE_API int (*RedisModule_GetNotifyKeyspaceEvents)() REDISMODULE_ATTR;
790 | REDISMODULE_API int (*RedisModule_BlockedClientDisconnected)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
791 | REDISMODULE_API void (*RedisModule_RegisterClusterMessageReceiver)(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback) REDISMODULE_ATTR;
792 | REDISMODULE_API int (*RedisModule_SendClusterMessage)(RedisModuleCtx *ctx, char *target_id, uint8_t type, unsigned char *msg, uint32_t len) REDISMODULE_ATTR;
793 | REDISMODULE_API int (*RedisModule_GetClusterNodeInfo)(RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags) REDISMODULE_ATTR;
794 | REDISMODULE_API char ** (*RedisModule_GetClusterNodesList)(RedisModuleCtx *ctx, size_t *numnodes) REDISMODULE_ATTR;
795 | REDISMODULE_API void (*RedisModule_FreeClusterNodesList)(char **ids) REDISMODULE_ATTR;
796 | REDISMODULE_API RedisModuleTimerID (*RedisModule_CreateTimer)(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data) REDISMODULE_ATTR;
797 | REDISMODULE_API int (*RedisModule_StopTimer)(RedisModuleCtx *ctx, RedisModuleTimerID id, void **data) REDISMODULE_ATTR;
798 | REDISMODULE_API int (*RedisModule_GetTimerInfo)(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data) REDISMODULE_ATTR;
799 | REDISMODULE_API const char * (*RedisModule_GetMyClusterID)(void) REDISMODULE_ATTR;
800 | REDISMODULE_API size_t (*RedisModule_GetClusterSize)(void) REDISMODULE_ATTR;
801 | REDISMODULE_API void (*RedisModule_GetRandomBytes)(unsigned char *dst, size_t len) REDISMODULE_ATTR;
802 | REDISMODULE_API void (*RedisModule_GetRandomHexChars)(char *dst, size_t len) REDISMODULE_ATTR;
803 | REDISMODULE_API void (*RedisModule_SetDisconnectCallback)(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback) REDISMODULE_ATTR;
804 | REDISMODULE_API void (*RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags) REDISMODULE_ATTR;
805 | REDISMODULE_API int (*RedisModule_ExportSharedAPI)(RedisModuleCtx *ctx, const char *apiname, void *func) REDISMODULE_ATTR;
806 | REDISMODULE_API void * (*RedisModule_GetSharedAPI)(RedisModuleCtx *ctx, const char *apiname) REDISMODULE_ATTR;
807 | REDISMODULE_API RedisModuleCommandFilter * (*RedisModule_RegisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb, int flags) REDISMODULE_ATTR;
808 | REDISMODULE_API int (*RedisModule_UnregisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter) REDISMODULE_ATTR;
809 | REDISMODULE_API int (*RedisModule_CommandFilterArgsCount)(RedisModuleCommandFilterCtx *fctx) REDISMODULE_ATTR;
810 | REDISMODULE_API const RedisModuleString * (*RedisModule_CommandFilterArgGet)(RedisModuleCommandFilterCtx *fctx, int pos) REDISMODULE_ATTR;
811 | REDISMODULE_API int (*RedisModule_CommandFilterArgInsert)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg) REDISMODULE_ATTR;
812 | REDISMODULE_API int (*RedisModule_CommandFilterArgReplace)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg) REDISMODULE_ATTR;
813 | REDISMODULE_API int (*RedisModule_CommandFilterArgDelete)(RedisModuleCommandFilterCtx *fctx, int pos) REDISMODULE_ATTR;
814 | REDISMODULE_API int (*RedisModule_Fork)(RedisModuleForkDoneHandler cb, void *user_data) REDISMODULE_ATTR;
815 | REDISMODULE_API void (*RedisModule_SendChildCOWInfo)(void) REDISMODULE_ATTR;
816 | REDISMODULE_API int (*RedisModule_ExitFromChild)(int retcode) REDISMODULE_ATTR;
817 | REDISMODULE_API int (*RedisModule_KillForkChild)(int child_pid) REDISMODULE_ATTR;
818 | REDISMODULE_API float (*RedisModule_GetUsedMemoryRatio)() REDISMODULE_ATTR;
819 | REDISMODULE_API size_t (*RedisModule_MallocSize)(void* ptr) REDISMODULE_ATTR;
820 | REDISMODULE_API RedisModuleUser * (*RedisModule_CreateModuleUser)(const char *name) REDISMODULE_ATTR;
821 | REDISMODULE_API void (*RedisModule_FreeModuleUser)(RedisModuleUser *user) REDISMODULE_ATTR;
822 | REDISMODULE_API int (*RedisModule_SetModuleUserACL)(RedisModuleUser *user, const char* acl) REDISMODULE_ATTR;
823 | REDISMODULE_API int (*RedisModule_AuthenticateClientWithACLUser)(RedisModuleCtx *ctx, const char *name, size_t len, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) REDISMODULE_ATTR;
824 | REDISMODULE_API int (*RedisModule_AuthenticateClientWithUser)(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) REDISMODULE_ATTR;
825 | REDISMODULE_API int (*RedisModule_DeauthenticateAndCloseClient)(RedisModuleCtx *ctx, uint64_t client_id) REDISMODULE_ATTR;
826 | REDISMODULE_API RedisModuleString * (*RedisModule_GetClientCertificate)(RedisModuleCtx *ctx, uint64_t id) REDISMODULE_ATTR;
827 | REDISMODULE_API int *(*RedisModule_GetCommandKeys)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int *num_keys) REDISMODULE_ATTR;
828 | REDISMODULE_API int (*RedisModule_RegisterDefragFunc)(RedisModuleCtx *ctx, RedisModuleDefragFunc func) REDISMODULE_ATTR;
829 | REDISMODULE_API void *(*RedisModule_DefragAlloc)(RedisModuleDefragCtx *ctx, void *ptr) REDISMODULE_ATTR;
830 | REDISMODULE_API RedisModuleString *(*RedisModule_DefragRedisModuleString)(RedisModuleDefragCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
831 | REDISMODULE_API int (*RedisModule_DefragShouldStop)(RedisModuleDefragCtx *ctx) REDISMODULE_ATTR;
832 | REDISMODULE_API int (*RedisModule_DefragCursorSet)(RedisModuleDefragCtx *ctx, unsigned long cursor) REDISMODULE_ATTR;
833 | REDISMODULE_API int (*RedisModule_DefragCursorGet)(RedisModuleDefragCtx *ctx, unsigned long *cursor) REDISMODULE_ATTR;
834 | #endif
835 |
836 | #define RedisModule_IsAOFClient(id) ((id) == CLIENT_ID_AOF)
837 |
838 | /* This is included inline inside each Redis module. */
839 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) REDISMODULE_ATTR_UNUSED;
840 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) {
841 | void *getapifuncptr = ((void**)ctx)[0];
842 | RedisModule_GetApi = (int (*)(const char *, void *)) (unsigned long)getapifuncptr;
843 | REDISMODULE_GET_API(Alloc);
844 | REDISMODULE_GET_API(Calloc);
845 | REDISMODULE_GET_API(Free);
846 | REDISMODULE_GET_API(Realloc);
847 | REDISMODULE_GET_API(Strdup);
848 | REDISMODULE_GET_API(CreateCommand);
849 | REDISMODULE_GET_API(SetModuleAttribs);
850 | REDISMODULE_GET_API(IsModuleNameBusy);
851 | REDISMODULE_GET_API(WrongArity);
852 | REDISMODULE_GET_API(ReplyWithLongLong);
853 | REDISMODULE_GET_API(ReplyWithError);
854 | REDISMODULE_GET_API(ReplyWithSimpleString);
855 | REDISMODULE_GET_API(ReplyWithArray);
856 | REDISMODULE_GET_API(ReplyWithNullArray);
857 | REDISMODULE_GET_API(ReplyWithEmptyArray);
858 | REDISMODULE_GET_API(ReplySetArrayLength);
859 | REDISMODULE_GET_API(ReplyWithStringBuffer);
860 | REDISMODULE_GET_API(ReplyWithCString);
861 | REDISMODULE_GET_API(ReplyWithString);
862 | REDISMODULE_GET_API(ReplyWithEmptyString);
863 | REDISMODULE_GET_API(ReplyWithVerbatimString);
864 | REDISMODULE_GET_API(ReplyWithNull);
865 | REDISMODULE_GET_API(ReplyWithCallReply);
866 | REDISMODULE_GET_API(ReplyWithDouble);
867 | REDISMODULE_GET_API(ReplyWithLongDouble);
868 | REDISMODULE_GET_API(GetSelectedDb);
869 | REDISMODULE_GET_API(SelectDb);
870 | REDISMODULE_GET_API(OpenKey);
871 | REDISMODULE_GET_API(CloseKey);
872 | REDISMODULE_GET_API(KeyType);
873 | REDISMODULE_GET_API(ValueLength);
874 | REDISMODULE_GET_API(ListPush);
875 | REDISMODULE_GET_API(ListPop);
876 | REDISMODULE_GET_API(StringToLongLong);
877 | REDISMODULE_GET_API(StringToDouble);
878 | REDISMODULE_GET_API(StringToLongDouble);
879 | REDISMODULE_GET_API(StringToStreamID);
880 | REDISMODULE_GET_API(Call);
881 | REDISMODULE_GET_API(CallReplyProto);
882 | REDISMODULE_GET_API(FreeCallReply);
883 | REDISMODULE_GET_API(CallReplyInteger);
884 | REDISMODULE_GET_API(CallReplyType);
885 | REDISMODULE_GET_API(CallReplyLength);
886 | REDISMODULE_GET_API(CallReplyArrayElement);
887 | REDISMODULE_GET_API(CallReplyStringPtr);
888 | REDISMODULE_GET_API(CreateStringFromCallReply);
889 | REDISMODULE_GET_API(CreateString);
890 | REDISMODULE_GET_API(CreateStringFromLongLong);
891 | REDISMODULE_GET_API(CreateStringFromDouble);
892 | REDISMODULE_GET_API(CreateStringFromLongDouble);
893 | REDISMODULE_GET_API(CreateStringFromString);
894 | REDISMODULE_GET_API(CreateStringFromStreamID);
895 | REDISMODULE_GET_API(CreateStringPrintf);
896 | REDISMODULE_GET_API(FreeString);
897 | REDISMODULE_GET_API(StringPtrLen);
898 | REDISMODULE_GET_API(AutoMemory);
899 | REDISMODULE_GET_API(Replicate);
900 | REDISMODULE_GET_API(ReplicateVerbatim);
901 | REDISMODULE_GET_API(DeleteKey);
902 | REDISMODULE_GET_API(UnlinkKey);
903 | REDISMODULE_GET_API(StringSet);
904 | REDISMODULE_GET_API(StringDMA);
905 | REDISMODULE_GET_API(StringTruncate);
906 | REDISMODULE_GET_API(GetExpire);
907 | REDISMODULE_GET_API(SetExpire);
908 | REDISMODULE_GET_API(ResetDataset);
909 | REDISMODULE_GET_API(DbSize);
910 | REDISMODULE_GET_API(RandomKey);
911 | REDISMODULE_GET_API(ZsetAdd);
912 | REDISMODULE_GET_API(ZsetIncrby);
913 | REDISMODULE_GET_API(ZsetScore);
914 | REDISMODULE_GET_API(ZsetRem);
915 | REDISMODULE_GET_API(ZsetRangeStop);
916 | REDISMODULE_GET_API(ZsetFirstInScoreRange);
917 | REDISMODULE_GET_API(ZsetLastInScoreRange);
918 | REDISMODULE_GET_API(ZsetFirstInLexRange);
919 | REDISMODULE_GET_API(ZsetLastInLexRange);
920 | REDISMODULE_GET_API(ZsetRangeCurrentElement);
921 | REDISMODULE_GET_API(ZsetRangeNext);
922 | REDISMODULE_GET_API(ZsetRangePrev);
923 | REDISMODULE_GET_API(ZsetRangeEndReached);
924 | REDISMODULE_GET_API(HashSet);
925 | REDISMODULE_GET_API(HashGet);
926 | REDISMODULE_GET_API(StreamAdd);
927 | REDISMODULE_GET_API(StreamDelete);
928 | REDISMODULE_GET_API(StreamIteratorStart);
929 | REDISMODULE_GET_API(StreamIteratorStop);
930 | REDISMODULE_GET_API(StreamIteratorNextID);
931 | REDISMODULE_GET_API(StreamIteratorNextField);
932 | REDISMODULE_GET_API(StreamIteratorDelete);
933 | REDISMODULE_GET_API(StreamTrimByLength);
934 | REDISMODULE_GET_API(StreamTrimByID);
935 | REDISMODULE_GET_API(IsKeysPositionRequest);
936 | REDISMODULE_GET_API(KeyAtPos);
937 | REDISMODULE_GET_API(GetClientId);
938 | REDISMODULE_GET_API(GetContextFlags);
939 | REDISMODULE_GET_API(AvoidReplicaTraffic);
940 | REDISMODULE_GET_API(PoolAlloc);
941 | REDISMODULE_GET_API(CreateDataType);
942 | REDISMODULE_GET_API(ModuleTypeSetValue);
943 | REDISMODULE_GET_API(ModuleTypeReplaceValue);
944 | REDISMODULE_GET_API(ModuleTypeGetType);
945 | REDISMODULE_GET_API(ModuleTypeGetValue);
946 | REDISMODULE_GET_API(IsIOError);
947 | REDISMODULE_GET_API(SetModuleOptions);
948 | REDISMODULE_GET_API(SignalModifiedKey);
949 | REDISMODULE_GET_API(SaveUnsigned);
950 | REDISMODULE_GET_API(LoadUnsigned);
951 | REDISMODULE_GET_API(SaveSigned);
952 | REDISMODULE_GET_API(LoadSigned);
953 | REDISMODULE_GET_API(SaveString);
954 | REDISMODULE_GET_API(SaveStringBuffer);
955 | REDISMODULE_GET_API(LoadString);
956 | REDISMODULE_GET_API(LoadStringBuffer);
957 | REDISMODULE_GET_API(SaveDouble);
958 | REDISMODULE_GET_API(LoadDouble);
959 | REDISMODULE_GET_API(SaveFloat);
960 | REDISMODULE_GET_API(LoadFloat);
961 | REDISMODULE_GET_API(SaveLongDouble);
962 | REDISMODULE_GET_API(LoadLongDouble);
963 | REDISMODULE_GET_API(SaveDataTypeToString);
964 | REDISMODULE_GET_API(LoadDataTypeFromString);
965 | REDISMODULE_GET_API(EmitAOF);
966 | REDISMODULE_GET_API(Log);
967 | REDISMODULE_GET_API(LogIOError);
968 | REDISMODULE_GET_API(_Assert);
969 | REDISMODULE_GET_API(LatencyAddSample);
970 | REDISMODULE_GET_API(StringAppendBuffer);
971 | REDISMODULE_GET_API(RetainString);
972 | REDISMODULE_GET_API(HoldString);
973 | REDISMODULE_GET_API(StringCompare);
974 | REDISMODULE_GET_API(GetContextFromIO);
975 | REDISMODULE_GET_API(GetKeyNameFromIO);
976 | REDISMODULE_GET_API(GetKeyNameFromModuleKey);
977 | REDISMODULE_GET_API(Milliseconds);
978 | REDISMODULE_GET_API(DigestAddStringBuffer);
979 | REDISMODULE_GET_API(DigestAddLongLong);
980 | REDISMODULE_GET_API(DigestEndSequence);
981 | REDISMODULE_GET_API(CreateDict);
982 | REDISMODULE_GET_API(FreeDict);
983 | REDISMODULE_GET_API(DictSize);
984 | REDISMODULE_GET_API(DictSetC);
985 | REDISMODULE_GET_API(DictReplaceC);
986 | REDISMODULE_GET_API(DictSet);
987 | REDISMODULE_GET_API(DictReplace);
988 | REDISMODULE_GET_API(DictGetC);
989 | REDISMODULE_GET_API(DictGet);
990 | REDISMODULE_GET_API(DictDelC);
991 | REDISMODULE_GET_API(DictDel);
992 | REDISMODULE_GET_API(DictIteratorStartC);
993 | REDISMODULE_GET_API(DictIteratorStart);
994 | REDISMODULE_GET_API(DictIteratorStop);
995 | REDISMODULE_GET_API(DictIteratorReseekC);
996 | REDISMODULE_GET_API(DictIteratorReseek);
997 | REDISMODULE_GET_API(DictNextC);
998 | REDISMODULE_GET_API(DictPrevC);
999 | REDISMODULE_GET_API(DictNext);
1000 | REDISMODULE_GET_API(DictPrev);
1001 | REDISMODULE_GET_API(DictCompare);
1002 | REDISMODULE_GET_API(DictCompareC);
1003 | REDISMODULE_GET_API(RegisterInfoFunc);
1004 | REDISMODULE_GET_API(InfoAddSection);
1005 | REDISMODULE_GET_API(InfoBeginDictField);
1006 | REDISMODULE_GET_API(InfoEndDictField);
1007 | REDISMODULE_GET_API(InfoAddFieldString);
1008 | REDISMODULE_GET_API(InfoAddFieldCString);
1009 | REDISMODULE_GET_API(InfoAddFieldDouble);
1010 | REDISMODULE_GET_API(InfoAddFieldLongLong);
1011 | REDISMODULE_GET_API(InfoAddFieldULongLong);
1012 | REDISMODULE_GET_API(GetServerInfo);
1013 | REDISMODULE_GET_API(FreeServerInfo);
1014 | REDISMODULE_GET_API(ServerInfoGetField);
1015 | REDISMODULE_GET_API(ServerInfoGetFieldC);
1016 | REDISMODULE_GET_API(ServerInfoGetFieldSigned);
1017 | REDISMODULE_GET_API(ServerInfoGetFieldUnsigned);
1018 | REDISMODULE_GET_API(ServerInfoGetFieldDouble);
1019 | REDISMODULE_GET_API(GetClientInfoById);
1020 | REDISMODULE_GET_API(PublishMessage);
1021 | REDISMODULE_GET_API(SubscribeToServerEvent);
1022 | REDISMODULE_GET_API(SetLRU);
1023 | REDISMODULE_GET_API(GetLRU);
1024 | REDISMODULE_GET_API(SetLFU);
1025 | REDISMODULE_GET_API(GetLFU);
1026 | REDISMODULE_GET_API(BlockClientOnKeys);
1027 | REDISMODULE_GET_API(SignalKeyAsReady);
1028 | REDISMODULE_GET_API(GetBlockedClientReadyKey);
1029 | REDISMODULE_GET_API(ScanCursorCreate);
1030 | REDISMODULE_GET_API(ScanCursorRestart);
1031 | REDISMODULE_GET_API(ScanCursorDestroy);
1032 | REDISMODULE_GET_API(Scan);
1033 | REDISMODULE_GET_API(ScanKey);
1034 | REDISMODULE_GET_API(GetContextFlagsAll);
1035 | REDISMODULE_GET_API(GetKeyspaceNotificationFlagsAll);
1036 | REDISMODULE_GET_API(IsSubEventSupported);
1037 | REDISMODULE_GET_API(GetServerVersion);
1038 | REDISMODULE_GET_API(GetTypeMethodVersion);
1039 |
1040 | #ifdef REDISMODULE_EXPERIMENTAL_API
1041 | REDISMODULE_GET_API(GetThreadSafeContext);
1042 | REDISMODULE_GET_API(GetDetachedThreadSafeContext);
1043 | REDISMODULE_GET_API(FreeThreadSafeContext);
1044 | REDISMODULE_GET_API(ThreadSafeContextLock);
1045 | REDISMODULE_GET_API(ThreadSafeContextTryLock);
1046 | REDISMODULE_GET_API(ThreadSafeContextUnlock);
1047 | REDISMODULE_GET_API(BlockClient);
1048 | REDISMODULE_GET_API(UnblockClient);
1049 | REDISMODULE_GET_API(IsBlockedReplyRequest);
1050 | REDISMODULE_GET_API(IsBlockedTimeoutRequest);
1051 | REDISMODULE_GET_API(GetBlockedClientPrivateData);
1052 | REDISMODULE_GET_API(GetBlockedClientHandle);
1053 | REDISMODULE_GET_API(AbortBlock);
1054 | REDISMODULE_GET_API(BlockedClientMeasureTimeStart);
1055 | REDISMODULE_GET_API(BlockedClientMeasureTimeEnd);
1056 | REDISMODULE_GET_API(SetDisconnectCallback);
1057 | REDISMODULE_GET_API(SubscribeToKeyspaceEvents);
1058 | REDISMODULE_GET_API(NotifyKeyspaceEvent);
1059 | REDISMODULE_GET_API(GetNotifyKeyspaceEvents);
1060 | REDISMODULE_GET_API(BlockedClientDisconnected);
1061 | REDISMODULE_GET_API(RegisterClusterMessageReceiver);
1062 | REDISMODULE_GET_API(SendClusterMessage);
1063 | REDISMODULE_GET_API(GetClusterNodeInfo);
1064 | REDISMODULE_GET_API(GetClusterNodesList);
1065 | REDISMODULE_GET_API(FreeClusterNodesList);
1066 | REDISMODULE_GET_API(CreateTimer);
1067 | REDISMODULE_GET_API(StopTimer);
1068 | REDISMODULE_GET_API(GetTimerInfo);
1069 | REDISMODULE_GET_API(GetMyClusterID);
1070 | REDISMODULE_GET_API(GetClusterSize);
1071 | REDISMODULE_GET_API(GetRandomBytes);
1072 | REDISMODULE_GET_API(GetRandomHexChars);
1073 | REDISMODULE_GET_API(SetClusterFlags);
1074 | REDISMODULE_GET_API(ExportSharedAPI);
1075 | REDISMODULE_GET_API(GetSharedAPI);
1076 | REDISMODULE_GET_API(RegisterCommandFilter);
1077 | REDISMODULE_GET_API(UnregisterCommandFilter);
1078 | REDISMODULE_GET_API(CommandFilterArgsCount);
1079 | REDISMODULE_GET_API(CommandFilterArgGet);
1080 | REDISMODULE_GET_API(CommandFilterArgInsert);
1081 | REDISMODULE_GET_API(CommandFilterArgReplace);
1082 | REDISMODULE_GET_API(CommandFilterArgDelete);
1083 | REDISMODULE_GET_API(Fork);
1084 | REDISMODULE_GET_API(SendChildCOWInfo);
1085 | REDISMODULE_GET_API(ExitFromChild);
1086 | REDISMODULE_GET_API(KillForkChild);
1087 | REDISMODULE_GET_API(GetUsedMemoryRatio);
1088 | REDISMODULE_GET_API(MallocSize);
1089 | REDISMODULE_GET_API(CreateModuleUser);
1090 | REDISMODULE_GET_API(FreeModuleUser);
1091 | REDISMODULE_GET_API(SetModuleUserACL);
1092 | REDISMODULE_GET_API(DeauthenticateAndCloseClient);
1093 | REDISMODULE_GET_API(AuthenticateClientWithACLUser);
1094 | REDISMODULE_GET_API(AuthenticateClientWithUser);
1095 | REDISMODULE_GET_API(GetClientCertificate);
1096 | REDISMODULE_GET_API(GetCommandKeys);
1097 | REDISMODULE_GET_API(RegisterDefragFunc);
1098 | REDISMODULE_GET_API(DefragAlloc);
1099 | REDISMODULE_GET_API(DefragRedisModuleString);
1100 | REDISMODULE_GET_API(DefragShouldStop);
1101 | REDISMODULE_GET_API(DefragCursorSet);
1102 | REDISMODULE_GET_API(DefragCursorGet);
1103 | #endif
1104 |
1105 | if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR;
1106 | RedisModule_SetModuleAttribs(ctx,name,ver,apiver);
1107 | return REDISMODULE_OK;
1108 | }
1109 |
1110 | #define RedisModule_Assert(_e) ((_e)?(void)0 : (RedisModule__Assert(#_e,__FILE__,__LINE__),exit(1)))
1111 |
1112 | #define RMAPI_FUNC_SUPPORTED(func) (func != NULL)
1113 |
1114 | #else
1115 |
1116 | /* Things only defined for the modules core, not exported to modules
1117 | * including this file. */
1118 | #define RedisModuleString robj
1119 |
1120 | #endif /* REDISMODULE_CORE */
1121 | #endif /* REDISMODULE_H */
1122 |
--------------------------------------------------------------------------------
/src/scache/Makefile:
--------------------------------------------------------------------------------
1 |
2 | # find the OS
3 | uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
4 |
5 | # Compile flags for linux / osx
6 | ifeq ($(uname_S),Linux)
7 | SHOBJ_CFLAGS ?= -W -Wall -fno-common -g -ggdb -std=c99 -O2
8 | SHOBJ_LDFLAGS ?= -shared
9 | else
10 | SHOBJ_CFLAGS ?= -W -Wall -dynamic -fno-common -g -ggdb -std=c99 -O2
11 | SHOBJ_LDFLAGS ?= -bundle -undefined dynamic_lookup
12 | endif
13 |
14 | MYSQL_CFLAGS = -I/usr/include/mysql -DBIG_JOINS=1 -fno-strict-aliasing -g -DNDEBUG
15 | MYSQL_LIBS = -L/usr/lib64/mysql/ -L/usr/lib/x86_64-linux-gnu -lmysqlclient -lpthread -lz -lm -ldl
16 |
17 | .SUFFIXES: .c .so .xo .o
18 |
19 | all: scache.so
20 |
21 | .c.xo:
22 | $(CC) -I. $(CFLAGS) $(SHOBJ_CFLAGS) $(MYSQL_CFLAGS) -fPIC -c $< -o $@
23 |
24 | scache.xo: ../redismodule.h
25 |
26 | scache.so: scache.xo
27 | $(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) $(MYSQL_LIBS) -lc
28 |
29 | clean:
30 | rm -rf *.xo *.so
31 |
--------------------------------------------------------------------------------
/src/scache/scache.c:
--------------------------------------------------------------------------------
1 | /// @file scache.c
2 | /// @brief SmartCache Redis module
3 | /// @date 30/05/2017
4 | /// @author François Cerbelle (Fanfan), francois@cerbelle.net
5 | /// @copyright Copyright (c) 2017, François Cerbelle
6 | ///
7 | /// @todo Refactor SCacheGetMeta_RedisCommand and SCacheGetValue_RedisCommand
8 | /// @todo Store values in internal data structure
9 | /// @todo TTL management : Create a regular redis key with TTL for value expiration,
10 | /// subscribe to notifications in a background thread to delete values from internal
11 | /// structure
12 | /// @todo TTL management : Create a sorted set with TTL converted in absolute timestamp
13 | /// as scores to achive a lazy expiration on values (check at read, and eventually
14 | /// background thread checking periodically)
15 | /// @todo Store connection details in Hash for persistence and replication, but keep
16 | /// connection handler in an internal data structure per shard
17 | /// @todo Return resultsets as complex values { {Metas} {Record1Values, Record2Values, Record3Values} }
18 | /// @todo DB abstraction layer with dynamically loadable modules (dl)
19 | /// @todo Cluster awareness (CE/EE)
20 | /// @todo Add Log entries for DEBUG, INFO, NOTICE levels
21 | /// @todo: use a connection pool
22 | ///
23 | /// @todo
24 | /// @bug
25 | /// @version
26 | /// @since
27 | ///
28 | /// Implements a smart and autonomous cache in a Redis module. Your application
29 | /// can query the cache for a value, / if the value is not in the cache, it will
30 | /// block the client, spawn a thread to fetch the data from the / underlying data
31 | /// source (MySQL in our case) while making Redis available for other queries
32 | /// (because a MySQL / fetch can be long relative to other Redis queries), get the
33 | /// value, store / it with an expiration time (TTL) and / send it back to the
34 | /// client.
35 | ///
36 | /// @internal
37 | /// Compiler gcc
38 | /// Last modified 2017-05-31 18:29
39 | /// Organization Cerbelle.net
40 | /// Company Home
41 | ///
42 | /// This source code is released for free distribution under the terms of the
43 | /// GNU General Public License as published by the Free Software Foundation.
44 | ///
45 |
46 | #define REDISMODULE_EXPERIMENTAL_API
47 | #include "../redismodule.h"
48 | #include
49 | #include
50 | #include
51 | #include
52 |
53 | typedef struct CacheDetails_s {
54 | char* cachename;
55 | uint16_t ttl;
56 | char* dbhost;
57 | uint16_t dbport;
58 | char* dbname;
59 | char* dbuser;
60 | char* dbpass;
61 | MYSQL* dbhandle;
62 | struct CacheDetails_s* next;
63 | } CacheDetails;
64 |
65 | CacheDetails* CacheList = NULL;
66 |
67 | void RedisModule_ReplyWithCacheDetails(RedisModuleCtx *ctx, CacheDetails* cur) {
68 | RedisModule_ReplyWithArray(ctx, 8);
69 | RedisModule_ReplyWithStringBuffer(ctx, cur->cachename, strlen(cur->cachename));
70 | RedisModule_ReplyWithLongLong(ctx,cur->ttl);
71 | RedisModule_ReplyWithStringBuffer(ctx, cur->dbhost, strlen(cur->dbhost));
72 | RedisModule_ReplyWithLongLong(ctx,cur->dbport);
73 | RedisModule_ReplyWithStringBuffer(ctx, cur->dbname, strlen(cur->dbname));
74 | RedisModule_ReplyWithStringBuffer(ctx, cur->dbuser, strlen(cur->dbuser));
75 | // RedisModule_ReplyWithStringBuffer(ctx, cur->dbpass, strlen(cur->dbpass));
76 | RedisModule_ReplyWithStringBuffer(ctx, "xxxxxxxx",8);
77 | RedisModule_ReplyWithLongLong(ctx, (long long)cur->dbhandle);
78 | }
79 |
80 | /* Reply callback for blocking command SCACHE.CREATE */
81 | int SCacheCreate_Reply(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
82 | REDISMODULE_NOT_USED(argv);
83 | REDISMODULE_NOT_USED(argc);
84 |
85 | CacheDetails *privdata=RedisModule_GetBlockedClientPrivateData(ctx);
86 |
87 | if ( NULL == privdata->dbhandle) {
88 | RedisModule_ReplyWithError(ctx,"ERR cannot connect to DB");
89 | return REDISMODULE_OK;
90 | }
91 |
92 | // FreeData will be automatically called after this callback to release the
93 | // memory, we need to create and store a copy of privdata.
94 | CacheDetails *cur=(CacheDetails*)RedisModule_Alloc(sizeof(CacheDetails));
95 | cur->cachename = RedisModule_Strdup(privdata->cachename);
96 | cur->ttl = privdata->ttl;
97 | cur->dbhost = RedisModule_Strdup(privdata->dbhost);
98 | cur->dbport = privdata->dbport;
99 | cur->dbname = RedisModule_Strdup(privdata->dbname);
100 | cur->dbuser = RedisModule_Strdup(privdata->dbuser);
101 | cur->dbpass = RedisModule_Strdup(privdata->dbpass);
102 | cur->dbhandle = privdata->dbhandle;
103 |
104 | // CRITICAL SECTION BEGIN : should be in a mutex
105 | cur->next = CacheList;
106 | CacheList = cur;
107 | // CRITICAL SECTION END : should be in a mutex
108 |
109 | RedisModule_ReplyWithCacheDetails(ctx,cur);
110 | return REDISMODULE_OK;
111 | }
112 |
113 | /* Timeout callback for SCACHE.CREATE command */
114 | int SCacheCreate_Timeout(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
115 | REDISMODULE_NOT_USED(argv);
116 | REDISMODULE_NOT_USED(argc);
117 | return RedisModule_ReplyWithSimpleString(ctx,"Request timedout");
118 | }
119 |
120 | /* Private data freeing callback for SCACHE.CREATE command. */
121 | void SCacheCreate_FreeData(RedisModuleCtx *ctx, void *privdata) {
122 | REDISMODULE_NOT_USED(ctx);
123 | CacheDetails *cur=privdata;
124 | RedisModule_Free(cur->cachename);
125 | RedisModule_Free(cur->dbhost);
126 | RedisModule_Free(cur->dbname);
127 | RedisModule_Free(cur->dbuser);
128 | RedisModule_Free(cur->dbpass);
129 | RedisModule_Free(cur);
130 | }
131 |
132 | /* The thread entry point that actually executes the blocking part
133 | * of the SCACHE.CREATEi command. */
134 | void *SCacheCreate_ThreadMain(void *arg) {
135 | void **targ = arg;
136 | RedisModuleBlockedClient *bc = targ[0];
137 | CacheDetails *cur = targ[1];
138 | RedisModule_Free(targ);
139 |
140 | MYSQL* mysql;
141 | // Init MySQL options structure with auto-reconnect in case of lost connection
142 | mysql = mysql_init(NULL);
143 | my_bool reconnect=1;
144 | mysql_options(mysql,MYSQL_OPT_RECONNECT,&reconnect);
145 | // Open the connection to MySQL
146 | cur-> dbhandle = mysql_real_connect(mysql, cur->dbhost, cur->dbuser, cur->dbpass, cur->dbname, cur->dbport, 0, 0);
147 |
148 | RedisModule_UnblockClient(bc,cur);
149 | return NULL;
150 | }
151 |
152 |
153 | // Creates a new cache configuration and stores it in a hash
154 | // SCACHE.CREATE
155 | int SCacheCreate_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
156 | // REDISMODULE_NOT_USED(argv);
157 | //REDISMODULE_NOT_USED(argc);
158 | if (argc != 8 ) return RedisModule_WrongArity(ctx);
159 |
160 | RedisModule_AutoMemory(ctx);
161 | CacheDetails *cur = CacheList;
162 | size_t len;
163 | const char* cachename = RedisModule_StringPtrLen(argv[1], &len);
164 |
165 | // Search for already defined cache
166 | while ((cur)&&(strcmp(cachename,cur->cachename)))
167 | cur=cur->next;
168 | if (cur) {
169 | RedisModule_ReplyWithError(ctx,"ERR Cache already defined, please delete before.");
170 | return REDISMODULE_OK;
171 | } else
172 | cur = (CacheDetails*)RedisModule_Alloc(sizeof(CacheDetails));
173 |
174 | // Initialize cachename from the arguments in the structure
175 | if (!(cur->cachename = (char*)RedisModule_Alloc(len+1))) {
176 | RedisModule_Free(cur);
177 | return RedisModule_ReplyWithError(ctx,"ERR unable to allocate memory to store the cachename.");
178 | }
179 | memcpy(cur->cachename, cachename, len+1);
180 |
181 | // Initialize ttl from the arguments in the structure
182 | if (0 == (cur->ttl=atoi(RedisModule_StringPtrLen(argv[2], &len)))) {
183 | RedisModule_Free(cur);
184 | return RedisModule_ReplyWithError(ctx,"ERR invalid default TTL");
185 | }
186 |
187 | // Initialize dbhost from the arguments in the structure
188 | const char* dbhost = RedisModule_StringPtrLen(argv[3], &len);
189 | if (!(cur->dbhost = (char*)RedisModule_Alloc(len+1))) {
190 | RedisModule_Free(cur);
191 | return RedisModule_ReplyWithError(ctx,"ERR unable to allocate memory to store the dbhost.");
192 | }
193 | memcpy(cur->dbhost, dbhost, len+1);
194 |
195 | // Initialize dbport from the arguments in the structure
196 | if (0 == (cur->dbport=atoi(RedisModule_StringPtrLen(argv[4], &len)))) {
197 | RedisModule_Free(cur);
198 | return RedisModule_ReplyWithError(ctx,"ERR invalid dbport number");
199 | }
200 |
201 | // Initialize dbname from the arguments in the structure
202 | const char* dbname = RedisModule_StringPtrLen(argv[5], &len);
203 | if (!(cur->dbname = (char*)RedisModule_Alloc(len+1))) {
204 | RedisModule_Free(cur);
205 | return RedisModule_ReplyWithError(ctx,"ERR unable to allocate memory to store the dbname.");
206 | }
207 | memcpy(cur->dbname, dbname, len+1);
208 |
209 | // Initialize dbuser from the arguments in the structure
210 | const char* dbuser = RedisModule_StringPtrLen(argv[6], &len);
211 | if (!(cur->dbuser = (char*)RedisModule_Alloc(len+1))) {
212 | RedisModule_Free(cur);
213 | return RedisModule_ReplyWithError(ctx,"ERR unable to allocate memory to store the dbuser.");
214 | }
215 | memcpy(cur->dbuser, dbuser, len+1);
216 |
217 | // Initialize dbpass from the arguments in the structure
218 | const char* dbpass = RedisModule_StringPtrLen(argv[7], &len);
219 | if (!(cur->dbpass = (char*)RedisModule_Alloc(len+1))) {
220 | RedisModule_Free(cur);
221 | return RedisModule_ReplyWithError(ctx,"ERR unable to allocate memory to store the dbpass.");
222 | }
223 | memcpy(cur->dbpass, dbpass, len+1);
224 |
225 | // Blocks the client connection with callbacks
226 | RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx,
227 | SCacheCreate_Reply,
228 | SCacheCreate_Timeout,
229 | SCacheCreate_FreeData,
230 | 1000); // timeout in milliseconds
231 |
232 | // Initialize the thread arguments structure
233 | void **targ = RedisModule_Alloc(sizeof(void*)*2);
234 | targ[0] = bc;
235 | targ[1] = cur;
236 |
237 | // Create the background thread
238 | pthread_t tid;
239 | if (pthread_create(&tid,NULL,SCacheCreate_ThreadMain,targ) != 0) {
240 | RedisModule_AbortBlock(bc);
241 | return RedisModule_ReplyWithError(ctx,"-ERR Can't start thread");
242 | }
243 |
244 | // Return to the main redis loop (unblock it)
245 | return REDISMODULE_OK;
246 | }
247 |
248 | // Lists all configured caches
249 | // O(n) n=nb caches
250 | int SCacheList_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
251 | REDISMODULE_NOT_USED(argv);
252 | REDISMODULE_NOT_USED(argc);
253 | if (argc != 1) return RedisModule_WrongArity(ctx);
254 |
255 | uint16_t count=0;
256 | CacheDetails* cur=CacheList;
257 |
258 | RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
259 | while (cur) {
260 | count++;
261 | RedisModule_ReplyWithStringBuffer(ctx, cur->cachename, strlen(cur->cachename));
262 | cur=cur->next;
263 | }
264 | RedisModule_ReplySetArrayLength(ctx,count);
265 |
266 | return REDISMODULE_OK;
267 | }
268 |
269 | // Returns a cache configuration details
270 | // O(n/2) n = nb caches
271 | int SCacheInfo_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
272 | REDISMODULE_NOT_USED(argv);
273 | REDISMODULE_NOT_USED(argc);
274 | if (argc != 2 ) return RedisModule_WrongArity(ctx);
275 |
276 | RedisModule_AutoMemory(ctx);
277 |
278 | CacheDetails* cur=CacheList;
279 | size_t len;
280 | const char* cachename = RedisModule_StringPtrLen(argv[1], &len);
281 |
282 | while ((cur)&&(strcmp(cachename,cur->cachename)))
283 | cur=cur->next;
284 |
285 | if (cur)
286 | RedisModule_ReplyWithCacheDetails(ctx,cur);
287 | else
288 | RedisModule_ReplyWithError(ctx,"ERR Cache definition not found.");
289 |
290 | return REDISMODULE_OK;
291 | }
292 |
293 | // Tests a cache's underlying database connection
294 | // O(n/2) n = nb caches
295 | int SCacheTest_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
296 | REDISMODULE_NOT_USED(argv);
297 | REDISMODULE_NOT_USED(argc);
298 |
299 | if (argc != 2 ) return RedisModule_WrongArity(ctx);
300 |
301 | RedisModule_AutoMemory(ctx);
302 |
303 | CacheDetails* cur=CacheList;
304 | size_t len;
305 | const char* cachename = RedisModule_StringPtrLen(argv[1], &len);
306 |
307 | while ((cur)&&(strcmp(cachename,cur->cachename)))
308 | cur=cur->next;
309 |
310 | if (cur) {
311 | if (mysql_ping(cur->dbhandle))
312 | RedisModule_ReplyWithError(ctx,"ERR Connection failed.");
313 | else
314 | RedisModule_ReplyWithLongLong(ctx,1);
315 | } else
316 | RedisModule_ReplyWithError(ctx,"ERR Cache definition not found.");
317 |
318 | return REDISMODULE_OK;
319 | }
320 |
321 | // Flushes all the values from a cache
322 | int SCacheFlush_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
323 | REDISMODULE_NOT_USED(argv);
324 | REDISMODULE_NOT_USED(argc);
325 | RedisModule_ReplyWithError(ctx,"ERR Not yet implemented.");
326 | return REDISMODULE_OK;
327 | }
328 |
329 | // Flushes and delete a cache
330 | // O(n/2) + Flush n = nb caches
331 | int SCacheDelete_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
332 | REDISMODULE_NOT_USED(argv);
333 | REDISMODULE_NOT_USED(argc);
334 | if (argc != 2 ) return RedisModule_WrongArity(ctx);
335 |
336 | // Flushes the cache first
337 | // SCacheFlush_RedisCommand(ctx, argv, argc);
338 |
339 | RedisModule_AutoMemory(ctx);
340 |
341 | CacheDetails* cur=CacheList;
342 | CacheDetails* tmp=NULL;
343 | size_t len;
344 | const char* cachename = RedisModule_StringPtrLen(argv[1], &len);
345 |
346 | if ((CacheList)&&!strcmp(cachename,CacheList->cachename)) {
347 | // First cache in the list
348 | tmp=CacheList;
349 | CacheList = CacheList->next;
350 | mysql_close(tmp->dbhandle);
351 | RedisModule_Free(tmp);
352 | RedisModule_ReplyWithLongLong(ctx,1);
353 | } else {
354 | // Not the first cache in the list, search in the list
355 | while ((cur)&&(cur->next)&&(strcmp(cachename,cur->next->cachename)))
356 | cur=cur->next;
357 |
358 | if (cur->next) {
359 | // Cache definition found
360 | tmp=cur->next;
361 | cur->next = cur->next->next;
362 | mysql_close(tmp->dbhandle);
363 | RedisModule_Free(tmp);
364 | RedisModule_ReplyWithLongLong(ctx,1);
365 | } else {
366 | // Cache definition not in the list
367 | RedisModule_ReplyWithError(ctx,"ERR Cache definition not found.");
368 | }
369 | }
370 | return REDISMODULE_OK;
371 | }
372 |
373 | // Queries the underlying DB to populate the keys (names, types and values) in the cache with TTL
374 | int SCachePopulate(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
375 | REDISMODULE_NOT_USED(argv);
376 | REDISMODULE_NOT_USED(argc);
377 |
378 | if (argc != 3) return RedisModule_WrongArity(ctx);
379 |
380 | RedisModule_AutoMemory(ctx);
381 |
382 | size_t len;
383 | const char* cachename = RedisModule_StringPtrLen(argv[1], &len);
384 |
385 | CacheDetails* cur=CacheList;
386 | while ((cur)&&strcmp(cur->cachename,cachename))
387 | cur=cur->next;
388 |
389 | if (NULL == cur) {
390 | RedisModule_ReplyWithError(ctx,"ERR cache definition not found.");
391 | return REDISMODULE_OK;
392 | }
393 |
394 | // Execute the underlying query
395 | const char* query = RedisModule_StringPtrLen(argv[2], &len);
396 | int state = mysql_query(cur->dbhandle, query);
397 |
398 | if( state != 0 ) {
399 | // Underlying error
400 | const char *error = mysql_error(cur->dbhandle);
401 | RedisModule_ReplyWithError(ctx,error);
402 | return REDISMODULE_OK;
403 | } else {
404 | // Fetch resultset
405 | MYSQL_RES* result = mysql_store_result(cur->dbhandle);
406 | if( result == (MYSQL_RES *)NULL ) {
407 | const char *error = mysql_error(cur->dbhandle);
408 | RedisModule_ReplyWithError(ctx,error);
409 | }
410 |
411 | // Build value and meta keynames cachename::query::value/cachename::query::meta
412 | RedisModuleString *valuekey = RedisModule_CreateStringFromString(ctx,argv[1]);
413 | RedisModule_StringAppendBuffer(ctx,valuekey,"::",2);
414 | RedisModule_StringAppendBuffer(ctx,valuekey,query,len);
415 | RedisModuleString *metakey = RedisModule_CreateStringFromString(ctx,valuekey);
416 | RedisModule_StringAppendBuffer(ctx,valuekey,"::value",7);
417 | RedisModule_StringAppendBuffer(ctx,metakey,"::meta",6);
418 |
419 | // Cache results meta
420 | unsigned int num_fields = mysql_num_fields(result);
421 | MYSQL_FIELD *fields = mysql_fetch_fields(result);
422 | RedisModuleString* tmp=NULL;
423 | unsigned int i=0;
424 | char* name;
425 | char* type;
426 | while (i < num_fields) {
427 | name = RedisModule_Strdup(fields[i].name);
428 | switch (fields[i].type) {
429 | case MYSQL_TYPE_TINY: type = RedisModule_Strdup("MYSQL_TYPE_TINY"); break;
430 | case MYSQL_TYPE_SHORT: type = RedisModule_Strdup("MYSQL_TYPE_SHORT"); break;
431 | case MYSQL_TYPE_LONG: type = RedisModule_Strdup("MYSQL_TYPE_LONG"); break;
432 | case MYSQL_TYPE_INT24: type = RedisModule_Strdup("MYSQL_TYPE_INT24"); break;
433 | case MYSQL_TYPE_LONGLONG: type = RedisModule_Strdup("MYSQL_TYPE_LONGLONG"); break;
434 | case MYSQL_TYPE_DECIMAL: type = RedisModule_Strdup("MYSQL_TYPE_DECIMAL"); break;
435 | case MYSQL_TYPE_NEWDECIMAL: type = RedisModule_Strdup("MYSQL_TYPE_NEWDECIMAL"); break;
436 | case MYSQL_TYPE_FLOAT: type = RedisModule_Strdup("MYSQL_TYPE_FLOAT"); break;
437 | case MYSQL_TYPE_DOUBLE: type = RedisModule_Strdup("MYSQL_TYPE_DOUBLE"); break;
438 | case MYSQL_TYPE_BIT: type = RedisModule_Strdup("MYSQL_TYPE_BIT"); break;
439 | case MYSQL_TYPE_TIMESTAMP: type = RedisModule_Strdup("MYSQL_TYPE_TIMESTAMP"); break;
440 | case MYSQL_TYPE_DATE: type = RedisModule_Strdup("MYSQL_TYPE_DATE"); break;
441 | case MYSQL_TYPE_TIME: type = RedisModule_Strdup("MYSQL_TYPE_TIME"); break;
442 | case MYSQL_TYPE_DATETIME: type = RedisModule_Strdup("MYSQL_TYPE_DATETIME"); break;
443 | case MYSQL_TYPE_YEAR: type = RedisModule_Strdup("MYSQL_TYPE_YEAR"); break;
444 | case MYSQL_TYPE_STRING: type = RedisModule_Strdup("MYSQL_TYPE_STRING"); break;
445 | case MYSQL_TYPE_VAR_STRING: type = RedisModule_Strdup("MYSQL_TYPE_VAR_STRING"); break;
446 | case MYSQL_TYPE_BLOB: type = RedisModule_Strdup("MYSQL_TYPE_BLOB"); break;
447 | case MYSQL_TYPE_SET: type = RedisModule_Strdup("MYSQL_TYPE_SET"); break;
448 | case MYSQL_TYPE_ENUM: type = RedisModule_Strdup("MYSQL_TYPE_ENUM"); break;
449 | case MYSQL_TYPE_GEOMETRY: type = RedisModule_Strdup("MYSQL_TYPE_GEOMETRY"); break;
450 | case MYSQL_TYPE_NULL: type = RedisModule_Strdup("MYSQL_TYPE_NULL"); break;
451 | default: type = RedisModule_Strdup("UNKNOWN"); break;
452 | }
453 | tmp = RedisModule_CreateString(ctx,name,strlen(name));
454 | RedisModule_StringAppendBuffer(ctx,tmp,"|",1);
455 | RedisModule_StringAppendBuffer(ctx,tmp,type,strlen(type));
456 | RedisModule_Call(ctx,"RPUSH","ss",metakey,tmp);
457 | RedisModule_FreeString(ctx,tmp);
458 | RedisModule_Free(name);
459 | RedisModule_Free(type);
460 | i++;
461 | }
462 |
463 | // Cache result values
464 | MYSQL_ROW row;
465 | char* rowstr;
466 | char* value;
467 | uint16_t count=0;
468 | while (NULL != (row = mysql_fetch_row(result))) {
469 | rowstr = RedisModule_Strdup("");
470 | for(i = 0; i < num_fields; i++) {
471 | value = (char*)(row[i] ? row[i] : "NULL");
472 | rowstr = (char*)RedisModule_Realloc(rowstr,strlen(rowstr)+1+strlen(value)+1);
473 | strcat(rowstr,"|");
474 | strcat(rowstr,value);
475 | }
476 | RedisModule_Call(ctx,"RPUSH","sc",valuekey,&rowstr[1],strlen(&rowstr[1]));
477 | RedisModule_Free(rowstr);
478 | count++;
479 | }
480 |
481 | // Set expiration time (TTL) on the meta and value keys
482 | RedisModule_Call(ctx,"EXPIRE","sl",metakey,cur->ttl);
483 | RedisModule_Call(ctx,"EXPIRE","sl",valuekey,cur->ttl);
484 |
485 | return REDISMODULE_OK;
486 | }
487 | }
488 |
489 | // Gets values from the cache (eventually fetching them from underlying database)
490 | int SCacheGetValue_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
491 | REDISMODULE_NOT_USED(argc);
492 | REDISMODULE_NOT_USED(argv);
493 | if (argc != 3) return RedisModule_WrongArity(ctx);
494 |
495 | RedisModule_AutoMemory(ctx);
496 |
497 | // Build the keys cachename::query::value
498 | RedisModuleString *valuekey = RedisModule_CreateStringFromString(ctx,argv[1]);
499 | RedisModule_StringAppendBuffer(ctx,valuekey,"::",2);
500 | size_t len;
501 | const char* query = RedisModule_StringPtrLen(argv[2], &len);
502 | RedisModule_StringAppendBuffer(ctx,valuekey,query,len);
503 | RedisModule_StringAppendBuffer(ctx,valuekey,"::value",7);
504 |
505 | // Try to get the resultset from the built valuekey in the cache
506 | RedisModuleCallReply *reply = RedisModule_Call(ctx,"LRANGE","scc",valuekey,"0","-1");
507 | if (0 == RedisModule_CallReplyLength(reply)) {
508 | // Not found : Populate it from the underlying DB and retry
509 | // Forget the empty result
510 | RedisModule_FreeCallReply(reply);
511 | // Populate the cache using the underlying database
512 | SCachePopulate(ctx,argv,argc);
513 | // Retry cache query
514 | reply = RedisModule_Call(ctx,"LRANGE","scc",valuekey,"0","-1");
515 | }
516 |
517 | RedisModule_ReplyWithCallReply(ctx,reply);
518 | return REDISMODULE_OK;
519 | }
520 |
521 | // Gets resultset's meta data from the cache (eventually fetching them from underlying database)
522 | int SCacheGetMeta_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
523 | REDISMODULE_NOT_USED(argc);
524 | REDISMODULE_NOT_USED(argv);
525 | if (argc != 3) return RedisModule_WrongArity(ctx);
526 |
527 | RedisModule_AutoMemory(ctx);
528 |
529 | // Build the meta key cachename::query::meta
530 | RedisModuleString *valuekey = RedisModule_CreateStringFromString(ctx,argv[1]);
531 | RedisModule_StringAppendBuffer(ctx,valuekey,"::",2);
532 | size_t len;
533 | const char* query = RedisModule_StringPtrLen(argv[2], &len);
534 | RedisModule_StringAppendBuffer(ctx,valuekey,query,len);
535 | RedisModule_StringAppendBuffer(ctx,valuekey,"::meta",6);
536 |
537 | // Try to fetch the meta from the built meta key, from the cache
538 | RedisModuleCallReply *reply = RedisModule_Call(ctx,"LRANGE","scc",valuekey,"0","-1");
539 | if (0 == RedisModule_CallReplyLength(reply)) {
540 | // Not found : populate the cache from the underlying DB and retry
541 | // Forget the empty result
542 | RedisModule_FreeCallReply(reply);
543 | // Populate the cache using the underlying database
544 | SCachePopulate(ctx,argv,argc);
545 | // Retry cache query
546 | reply = RedisModule_Call(ctx,"LRANGE","scc",valuekey,"0","-1");
547 | }
548 |
549 | RedisModule_ReplyWithCallReply(ctx,reply);
550 | return REDISMODULE_OK;
551 | }
552 |
553 | // Module initialization
554 | int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
555 | REDISMODULE_NOT_USED(argv);
556 | REDISMODULE_NOT_USED(argc);
557 |
558 | if (RedisModule_Init(ctx,"scache",1,REDISMODULE_APIVER_1)
559 | == REDISMODULE_ERR) return REDISMODULE_ERR;
560 |
561 | if (RedisModule_CreateCommand(ctx,"scache.create",
562 | SCacheCreate_RedisCommand,"write deny-oom no-monitor fast",0,0,0) == REDISMODULE_ERR)
563 | return REDISMODULE_ERR;
564 |
565 | if (RedisModule_CreateCommand(ctx,"scache.list",
566 | SCacheList_RedisCommand,"readonly deny-oom fast",0,0,0) == REDISMODULE_ERR)
567 | return REDISMODULE_ERR;
568 |
569 | if (RedisModule_CreateCommand(ctx,"scache.info",
570 | SCacheInfo_RedisCommand,"readonly deny-oom fast",0,0,0) == REDISMODULE_ERR)
571 | return REDISMODULE_ERR;
572 |
573 | if (RedisModule_CreateCommand(ctx,"scache.test",
574 | SCacheTest_RedisCommand,"readonly deny-oom fast",0,0,0) == REDISMODULE_ERR)
575 | return REDISMODULE_ERR;
576 |
577 | if (RedisModule_CreateCommand(ctx,"scache.flush",
578 | SCacheFlush_RedisCommand,"readonly deny-oom fast",0,0,0) == REDISMODULE_ERR)
579 | return REDISMODULE_ERR;
580 |
581 | if (RedisModule_CreateCommand(ctx,"scache.delete",
582 | SCacheDelete_RedisCommand,"readonly deny-oom fast",0,0,0) == REDISMODULE_ERR)
583 | return REDISMODULE_ERR;
584 |
585 | if (RedisModule_CreateCommand(ctx,"scache.getvalue",
586 | SCacheGetValue_RedisCommand,"write deny-oom fast",0,0,0) == REDISMODULE_ERR)
587 | return REDISMODULE_ERR;
588 |
589 | if (RedisModule_CreateCommand(ctx,"scache.getmeta",
590 | SCacheGetMeta_RedisCommand,"write deny-oom fast",0,0,0) == REDISMODULE_ERR)
591 | return REDISMODULE_ERR;
592 |
593 | return REDISMODULE_OK;
594 | }
595 |
--------------------------------------------------------------------------------