From bd7d2d675a1a2021f3315e4a8f7e2c22b954f10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=95=88=20=EC=8B=9C=EC=98=81?= Date: Mon, 19 Sep 2022 11:52:06 +0900 Subject: [PATCH] =?UTF-8?q?rdkafka=20include.tar=20=EB=94=94=EB=A0=89?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rdkafka/1.9.0/Linux-x86_64_8/include.tar | Bin 317440 -> 0 bytes .../1.9.0/Linux-x86_64_8/include/rdkafka.h | 8378 ----------------- rdkafka/1.9.0/{Linux-x86_64 => }/include.tar | Bin .../{Linux-x86_64 => }/include/rdkafka.h | 0 4 files changed, 8378 deletions(-) delete mode 100644 rdkafka/1.9.0/Linux-x86_64_8/include.tar delete mode 100644 rdkafka/1.9.0/Linux-x86_64_8/include/rdkafka.h rename rdkafka/1.9.0/{Linux-x86_64 => }/include.tar (100%) rename rdkafka/1.9.0/{Linux-x86_64 => }/include/rdkafka.h (100%) diff --git a/rdkafka/1.9.0/Linux-x86_64_8/include.tar b/rdkafka/1.9.0/Linux-x86_64_8/include.tar deleted file mode 100644 index a6b52af06013e6890a309b7cbc84150adde13ffb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 317440 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�<hr)x+EO&I^|bHSURXHmHNt#BH*0W1ZRU$C4u=a5FgD#=*+7WCk#rth%n_L?lQFuRsS#ETbiU8En_-nfk(-T|ErkpqC7LV& zkUZ8ywlqfy^NG1CRaywdY^4$dLHQ0WrKIm!KV0WdVFdNemzH_l>QRE+IBA*&g6=7H zRmCu3i>mt2@U4)~N@Zi(sUpFZFZ7NJ+sI0gacreFX;Jg1hxDp6Wm)SzO!GEEnu!6L z17Nq|aVHQb2$l`I97>pfHHTpjdMf89!CnvSTpUtejfnQnd3i1euaOPkj&059}j z+IXbysL0#rlu%*<>+F!2z~d#2jF1TGN+JhQ;;RW-8m;Wv;3#W5|1>(~7;epF1Vhp= z$uK9}&vdpJPe59kEfUC8#bgumAz6eYok9t#vQ9tkWKrqZ)EOEX)S;24*SJY|q-b{j zwp?OtY$$NJWSs_^seItKYkxjMB&D7smbWyn(nO_&AheI)$IB+|yS0PrYe|W?%o&N% zM8D@p$|%j*w@@|mUpc%|Z1^V9d&F3lzS1+!qNl-eB*fZoh>6muU_mko)mBh3xuKwK zRO;njHi?!X+@nK5|$gcye(A!RujcyJ4T^WF0bK;M$pZ>*v zycxdUeLj}feFcc8MfhbPln8Cw9#Gix{lU=Feg%$S-V+YkT2b47;_z z-_&xs9%wam1{3`H(PuFr39y)##$!w&R89NVuo?URJ<6T^l*3*8A(I;he#uQEW<<{w z-XL@zL|kb~0#OEoooG;aR0@H`tqm2viBhwqKC;MV1zNFxYH-rXQgFaiR8uy6TpcH+mDVB@aYZuu%nPQ7%Ddc-?9e!6*_pb0R6^r(kBZj+H!5$ zX@ocZL^KMdagf0(thpW1x(@AZOloW^ojZG1 z0%p21`sn?$=6QQZfNtqMHGx1`15`1sF;nS-522FG4sB`Yw|e9q0@<$6hbaa3TzE<_ zICj+a*}G!ixzwuvwctlp->5amwL5iSJvc_Ny~JZ2JhN8F*A2_?vtlcZhFVGsUx@b;1&3obj2-gu3TSNZe1!BgueK155^t#IhH3Fe~PbdFKP zHIh+&X`_tf6iJs^8!t&KZF%XR>?LW+y&7KYbpm*QWiNd#UjOR1#I)ry`6I|F)^fcW zRBIhguIr6RMUCCtzdzx15|^u~;7T8oaFkXwlN>NVL|5*I#D?Ltby|R!55Zu5f#dq`_RuYL1|9Uuj{w*MGc)PFA z@D%M_ze10fbNv-olQ9=9>3)O1+67*QuP}VWIo-boHT-#c%<R;+n%QOKVLN4u*@O#lN1Br?}DlNA|{EEzGJw_TsScXPHj3J-c z7_P+is=Qsu4*4#20gXNg+C>BpWnCUbQm?DNfUWTz1}<>`$As)>Y}#rKew2a@ zSew9~I7e~bQ2|rKagP$1dVr+`>V;YxFSLqa8s8&+dL%ZF1wnL6dey+xBc&o3?yCK8 z%gyAQoqE#MPMYhyRkroRPVcWnLbCv-UH}QmaIu;ed~K1^DhugA;vtN1eZWIW!V>Cc zRfl2CFqhpJ)UeTKrt7Q&l~95fU^oNH!1`jI>k({{D|DNJgC-Y}7Us?lfdW*&EuSra z9c7eq(uwoRPz5vijSC0ZI07eOBmBzCF0OqM4x!GAZ_USc!6a>jq>l_cuCwu>`loPy zui;s!pXv`ka~YCV;{JH&;9wjrUJ;Ek^sz@astWX+luIh?c~I?uVXgL1RnKkU(hj)e zUvqmq#S|uJ9a&d2yC9N2*9u#aQjJG~PkYfO28-AzcW2|5WLG+4WR{Z@Gi%tU*!iBUANBzAa?)G9{be$bH&%rT(qhe|;CD=25Nt37m*8 zMwgcX1zZGGFU2fJ`A|qu=L#?giWFvFyFsMnjZ5wFZC948T!uirypH{4x{m|18Sec+ zuOyx=qpF?G7cytr)@vhVRepP*zOlk|!vn~+n8YDq6^?Q8K@!8BMbxKFg_sh^*Uzc_ zqNiacs#(2YI|~^T?E`#^^`yM%F@0Y}60^;?Ch&5BmTl-afebt9A3?Quk{gjr4@-vl z_8pXU!Y)sSgTRp%;Z5PZ+EguU-syw9qI@ZVEQqPb?KdO5*9Lw9FBm1VN4Mt_X3Nc9 zN37Bh!Ax%E$Y{fozRP-zHJ6hk`phEV*|CRzRIojljSvxrl|#X+*2Z&bK}h)o>Z%tY zbTQg2IuF=HNl(jJvI8XeXMo$2v(WGhUPkcy-#yuW`gHrr_7hCT`JTO>TwfB#iX6l& z(8VCAi0nul=4hcR0X?V}>S)6zpkS}}x4@-kSiCu`lL>4}8b zNY~78BSUPU<2$)udbt+zJ|HOVS&D4%#T;S~?TPZ*d^O&Mpm|TE2Kg-d#h|v{2F4EL z($gr91)@Of6f8_~NCu79WW;SN2(^oWN9wU0dk{q)&B{F-z~I)16g1BE<()3(5LDrs zjt47*i)qo-XHQLoDh1IiS*djKSdJ)vgX}W=aOM4rFH!h9rem5TrwD4nH$yTwa~T zaZ4?Cp{Nlbhmsp=8l5Zr0#~A@t*citM=_-`L3rJrFM840+M(+WvTCo^4)s-qDA4te zW}B&b`XtJ=E`Zhi>D2GUeAt(9*-J%7^;ni0ZRD#}RAUQ;X1$$(ZkC$=$fSGL_;b3G zjUCX3RJKYVVsB02wHA^=0RiDwJ1U#o*+O&_uDWC=5`2p)48KHNwRvU=^{Qca=wiY_ z(;~V~SO9Rzo=RK~l#II?c?mC6?Bx9YO2 zuA$Djs@MRSzU)KJ?)hwRp#iW0?t*l7iu;(|lRlX$PZZePL6ve9 z?G)wPKv6&ayX2*HDp>7H?M>kKWwTYA^i#RE3$(W`ri;bobc*D?=p1o5>HJdE>3>@J z{&z5!tbCKDZF>DS6a-|j4k=eYQ*>1TaWl#;7QI&QkvpJea+8N+Fy37o*oRNJ?I!0 zmCH913b$~Z3r;Ga=u)#JjIWo0f$j)c>-FN}oi{HATW7P=v&Zk*gP)E!Zr8+(f7iz_ z9`x$hW8HFkvg|DDc4;iZIn=D<1`b}ru3=}}vSgDA6T1yr-qsR=n=J?=-O?{lEP9*CUhD!%@PImpkr@`hL! z!YP**jM>VUk>48EIcT~x+gMHL&I>rRZ*TDG{T{i-`uSsR08kWb{WX?!jD}byAKL3# zbK<@l%+|KNDb^`&a>0%B>NQ%vk+aL^KT686g3sxxnQ^Qi0!A`QR>m6&%a|F#)hI5x zoHjajsu0IXnA7)^>Z;&!xZCF<6VBjznp0yFy14V$Ww0S$UV9$4z=%tT!P7n%qyfHS zC%-L`4OQB=taLDVuE{R>b(lxmO7S)QmJXA=2A1+4{0w~Ar@R*pu zKT@N!gPrGl2QLo$9Otdby@*z-nZ6BRm+8Fwi63`k3i+>%l@>N^>c0DgP19(hY1^(% zmxa5jUxSiB2HN5EinwIdHAJRSNW@aBeX=i|LvwSdJIOv7z0E?`GNeU~tz8?0D8ZMk!v^5OSOMs^FL5G@G+nBCGFf%td_PS74JY609>Zd3x;E-DU*;uQ|pGTSP9 zU0I#z*ly!@nrqcYyOTzC6p5jFn2Xcrq#~J}+XMyEf!QVEPg#@`59@!{zAgDu6MdeW zSsa0tC4*_!wksvTjC(m!@~9ip_jaRn^~H-aFG7$oc!NYenM?l)lORds?v7t+i4Gv~ z9X}mbzQEx&+5K&N9fo^dTlo4p?Rz!twcm-XtIIBf13X8SBUspO^J%Y$#lo8z?F9w~ zLo-X%?IF@!fsoGSV425mFt=CH%UBnfezK;zV$7VDZbMxkaXon|K1DApJ=y5;)=Cpy z$JH2%XFgD&Rvh5cz9x1=qRmF~>g0fvI4hGI@swDw5dKxp%$RU>Cz}`|wX!idD^7J& zH=dU%%i##0+|n-bn7L-6Aq}8UVJ$!VWG0>*PU*JrT5)M=pl^h@HZky)acyDS)e-3~ z8eK`;D%@h)``B*ra@$}-{W`_?3vurh8)in`E9Wi5Y6CQkv}$lyHnFaQ`EDS=={vNv2F@N~iAPphurd( zK1bXb*VbGM_V+A^dQZEEYvfpFJHU~{Ehcg|hmE_b!PEuZ7%~<=L9&W~&L?}BR9$a& z7%)=zY{?2FU>nz`_5o*!z2);8(q`4goMEmn2K%KQQHXl_$_*65&llniyDNU#!`X3h zKDykdliBnfmFUc;GQ;ic2f@6`+H=?|;wc_LOg-o@{<_``(iDhqj8!z))aE>Ni}MFDYT4w9G9g;(>-V@X7%?&_i0SGItx zFw-=iJH%*+*d@Z0qV4tD!%;KgoF!CEUK6UxnkPwRSo~1Z>;msIJwSMZ&;R_N{r#f2c1Oymsto^RVndyEn+h2{@hSk4f z5^ex@^l!!o=w+DnbqTr<+tWcn=KQOUBVmHaK3M@z>BbQpkmoCUyL|dmrm14H zZV^+*NqMweW?tQ@t&b;1=f*3gCKx9vL`g0LLrJy}eYvDe#$~ZVp$LrUyC>f^&n2br z@gDvHozUcr^XGDmYj||Nhctl5H`(I>JAn8>E&HdxGj;j6v1H2^Q~VsXzF(n zbut)mytj|e4Wr$?x~qlu_*m5xqkdPKSBP=tu84O-*`-yZB*QjpXjy?%FB7y#$!n(k z_ZOEyH+=ORCD(I$iF9hfr6 zuMH6UXp>R&&L|n>H#UlWw8<#LS7_a#3;O54D?i#~kQY0L&kuIL-T_wrB|x!{HW}yn zD_D9v2jk&$Qsm%wK@$q_hc+4C;k4$Thm9GYK4}0S&uub^U^L#W*YMQYqJC&2 zWbeM%dA+|k;$^BzdW|WZJzCFgvIzE0$*n9iw{}YD^({vx5Uc@$NW(Y*Wnx)<|00U3 zN#$H?)+c)|Um?G%u`K}b>@N@rA#hzP@1qcKw%}pA5$Z8}iRSv1)$$oI+3>vtbaUus zQW`?~_3q&z#k91xv5vmrHz(P3vbs6K{Umk`?^3bdob1I8yJQu~{bIIY-Qs&_0<~(E z3Ge1W5czxGVIY^^-{3?lFFBP_Br!}$!JkTvG>7S+?s{b(K;nf0dN&0Eoj0ZzVy}Z0 z?T3`?8}oG%e@DGAhI3of;)wUia%+Xjz==4cfsN$`Hd^I(>IOB^dZe1pc5NO1y-3eG9n@*!t27daSvFA`JnM$EjjDXea4>;2=>Po22 zB9$zEvNgqiZg0n#+C;v5v^;O2@XAz5P*$py1igIV023I>$~B0+->VH#cEf0o=|qu; z%STaMDuph8%Cc0toxTzZ`pA5GL3}pb0Z@%2@)>FkPvxbz(n1FJ`Fl8?3r}n z!2F=}4t?7R#v&Jj<#6avTS|9EMad(4LF&O`wz9gx$?c^yXOQ6#bHc>}U~!9pn`jX3 zM%0@;{~&+D#%|IJr+}8l`C{seypEc zZft32M$ZoO zJ<#hisS@R#kh}D&dL!Q(8t&#>(k{O#Qtg=_SiZxY2Whh%v_d(PNBEB{UEuCvzqGLx<^EV%{ zBB+xd>k6PH=%B6)SeN^r#j!SPAivgK2h{8siZ^H=pxQJN^d*dD-%wjpIgP}auvVd} zNbQNuPYHQ&OXnW6=A;YCexXhHvIF;{ROk|g3KVUYl!+}M`uBKr(PYZ@Qy4|ZTfdLC zi&3##C**Z#bf}`lIQMYTUJ(8`80!}a1jc}@JU1t zj-)CvZ?bUo>VT8;Ysol@lFq`c%`HQoyd+`c{CJ+iQE zWG@h6kXDJ5vm#n%$=S^udXw{n8i@lFtgee`=;x+5hNRGb;3_ggCRfwxcgv)*4{?tJ z6T$s@Orm4bB$gGn%;rCeJRXL%(vZg)x>XXn>$weNa?s@xsqDk6_={Um=xb^mtMr+L zR8g%t)^A)GSp0LP$fa7c@ia6NMzi|Slv@z|c{@JE39frf&2WCkH?xZ=t|~+Pn#WI0 zChT<;{boPO0WmEN=_@IH!$Fvf`^d_4hNaN9?x{sdY@>TgtXBV;l_j7{Bxw0~69LF` zazsKoPQ6z6F-Ww$=@bWZPKFdi)%RlBee9?6$#&&nQ@Uy@L||4KO7m5wNrE<(rH`x- zv-al3C*)gVwlSp(*&4Xa+CPey~0SA8z$sljLjYPV3{&i3xDdrOH#C9t*m${Fva17)+*OHBOuhyg#4%`e;u7D z^EC0`1nxHJ(x58F9)$ng8CoSqm1_cIj-IW~#`3|7DR9(_#>T!_@pM)+g&?1|cA||` z6|+ue)!;NiuX4=VgDPa$r3xPvrQV(?owMf75I-${>==i4_$4Qcz35{S_=^Ko9uQ#& zP0{Y)Xp|v%0B~lx;jgTA#QdqBnIra7GAUl|jCT4P6fXboV@`zl0l6D16=OnH z*%HPBzWqNr2dtxn^3ZSJ9q#W6dp%=iyU3h|r^Wt^hr`2HU+xXxj=ueRXLx|B;+y&R zI8(~r9~aY`30p4e+o7RX+T`-~SbIB@?=CP9ZhAnSgN|$8$X!tg37_0xF6obkx&n#< zL`~={POfL?cWB-|_+Q^0qCVyVSBv7J*y;6baz6XNdU6Tj9z@{fowJ*ptHoDeet9!T z_pj~Q^yXxHetr7o+0DiIm)9pp|NQHxzy5_esgM5&osRjEg;6e!(arD#y@xKQ;|pGP z0eA?vLOcEQ74Lv}M483@lql%ZdorTmn#_NJ)?&EL8gd-xhU032!Srf=bk>+GQcGh= z>(waII<O}0 z&SyB%Mnx+|0k84xOB}!X<-b(KAtBx7l&Fdmz>aX*Qjdf?0R`3+XT58KW51z2`oZPMUu+Fu?SDIzPWAjo_-@I4L=n_2rLGz!!jcik1lLJfdxuHQp~CUU{)YX>zz0G_`3 z?YryWS6_5aQ{97yDk>glGSNM`K1E{=sYy4cj`8t}Rr>sE&uQN5jmEpL_h0S2#$gX! zev7GElnBCi<(6lP)}B#~_wIIfe%yEF!_g4OoR-X&#zyPWS0$iV)0|lz z%dhDx#<1g~cX%r~NhNPc8PD5Y@>Oqn91sq2Ij~_iwHyHzdG8GORehnrsLB!-HSb*G z_u1vmZ+?X^Rc%VFwJ!5?cr~=X7zP`?{Y8kA^){{9v+;(=*!8msQdH`$eb}cc8lvqk zwwOc^4lSnQ+6V{ScuX49J}yE=eVjmrGnt;9DiRuV0ota6$mztQz>OhCB5OGptWSaJ z6R8yh(v*1!zGyYazG<-~Pa7NXIgopth-yXs0f@=zl5Ggd?>RSurAq`$2 zbObg`t}#Mi4!weix|uLxy7a{4e0wIo6Rdxr z1WQ9T|MdV~6%w^2?nOei^HadJngV#w8}V|zv@=hpf}@)W?|LkIW;p;ahJrv=;OPhx zBw7uVG6mebyDLnGOC14$pXUOANM_-h4qD$w*OP@f#e97T|DPXCfkN}>(&X^#+E~;3 zAS1$q%(&i7MM&y*0VyJq?!TC)2p{5@s3nY;l0d{*d}N%);E_4O8MkUOcn8PmdxFV# zv2vK<#3F|=;)qoFMk4rA-mH{Wl^MxVyeu~lY@8sPaH&N4Y z#X_39LR!}?(+Akw98=5DGoPA(mHi5Wma{1xq~wZcPi|JsB%nva#L*V?H8$O@13QI7 zC#QKhZ^B~2c0u*KDIU+b){by;Xrsv|7br>-&+%-+qc^3w5d7rRxzJ64)>knE-r{x- z!wBqLp||E%oB`YKrf`XY=xYWfd|Tg5wTryQC(uy7Ih)8FXQkkJlDX`jijs2CDS;d8 z(jb@PEK)!!O##7x24evlEVBS#(+Vle;{fmGw}`1--#r>kw@nN!110U=@wWrF z0uk5F_v1kYxZZ_F2O=K5tx$q-Kz!1gDf)3&Zh52BWK>bF>_H@58|;zT z^+=)xg3?$JD4YbAIh!dGdZ*}M>?A{l#zETCO9l+K;gig_b&(XM6m$6UWOBaH1i(ld z?4xO5p5z#k?F3JQ0cG=pZxU2E9)dnEfQ$fn2X+H8HDJdOU{fyOoKX`UmmAFpCou7k zgQtM0+7jGPckI2r;lyVj3V$z_ICqj9A8(c|Ag8A|aFbIM=@TH98 z^EJEP*O`zdqE@sxgVo3j4)fi$SO>T;@M3tC5TN0%gL)pRcwI@PWsxF^l>sT8q5W=V zl+Bec5P}p2r&U0RqEn=hl+x;8tLUy{Xi#8?5|%iIz7WXj{D($4dP9nak-E5oX;x-_<3`tb==b zl{0Qa;w?+0i#uOT=1ILR)kswlg=g`f(va`IDsJMrV+#wuGp2orZY4n>4}x@ZEQ>1g zs7tO*PI0%D1I3X3;DBc|OC(f6ABS^jWW_H-j#v$OL&${3d@ruZ9g!+C{348n!=ADU zkk@87sXvOR0U%eegIeu7^pkU>yev?hoKH?g`Y7^o^&{B|OFc#&@`w|6tyRVye%iF`AX(b6kapb5F8JhlAOsO7YL+skc{ZIP9|E0;&+q<~s~#<% z!S+Lf^$_tG4XaSn#omh?Vv;EGb}b~T1mqerp@&Y>j10x$;_O!L-o>fe_W~t{3e77< zmFKkRpiO}^kRReSxc^tSYEqtdJx!@@`zL_WC(B#Bl&bZiO(`0pTV7m4#}vVo|7TNi zEeRR%b9z31hs_Nxk)h&B^P~B(8n7=jiIQ~GZV=^kJv%*x=mzC;KpMs555(06TMJzB ziw`+j8iA2-C_-8`gWWfygS{7TrN`Iy1zb&lc|j*5U73s&Fn0oW1P4N!+Q$oUFrzXx zg(B1=CY{|Sr0iUs_t(tO_~mdke8sUanYelcTvo2Sj*3oT547l#3aV8djqutW%cpbA z_3IGwEW|z368KP!U+w0QH87`-;pIigdZnYj#mXgO41!mFP%$7V1^D_Gkc=3CEah^F zL_2UR{6~fc@Aaz zCj~0AuPmU>%rYK&cY8|LOJ>h3Ni3Bcu^AU5G{Bl=$EeqE_*aMpiVV&W#0w1sICspO zT1L;&aDfXx;r`f`2;D_U#0&#c5^N$L>g@U!;_opQk5P%61f4MI2%nu!FCmM0REuUN z94Cpx5o3IbLX@ut7HCb|c5pSmBOE<;g``*Zg%+0BSJ*6iw9qmY73_$`E}K9)L$r~- zbvgJmVvxV^hoj%=nHi0v27$@;u)tYz?45KR|qgCEaTg=a{| z*kINjA~z?lK0<4G*1zD)RrkD=3A}8^2CT>MYH@$jmOh^6A%v%MkcaF^{>C8q@4=T} z8tpL1mw7vAAaRwgnEzy9(SS#bF$|n~-3fJ#mpT^l3^ZNuLNj5sS=3r&>VPZ0>k~0Bn$x3k@TrR2}>^iyi zW9YSyx(pu77k=c&0jLq5ba& z?5!RHq>A;{c@{Xqx8`39BL-HU(=4WI1(s18Ne9IqifoUw`t$}6u@$Kf+*BCp8(xv< zoX>@Z$W-^$Ksse43yQ5x#BxU(61>=@^{RHnY@i@tWUC-G#6&wUvx!H$uXkRI_uh`E zlPNe7WrI#4c2NO(Bo%FtTjX?#FVGVbp}-m?CWxxuy)B|mLS~=Ww3K^tDLl{ja4GEW zo8f5hV9ery;U9;)uZCa0+UWqV0aAb(ChZT9;^NzG1i+i;Zx0UGS$A|Wd~>L6hz~hE zeZIEPsi1f-h|$_7RWu)rk#W>vrJcbI#7tF9(0F`u%(!OBEmiP<6{NyYXiZ6a-42Z3 z;?jrx{k;QJn~h)Y3`cJd0{Lo7Z!JED-^{(Q=CYEoOhCwl|F#NZJ=uQx6mFdeM`TH0 zCNT~T4OWe`0kmF8k@jj#l1>C&R9-bntsu8e*la`8LM(Y|j0~ZReq4qD(3htTsn}kT z@^??1lhkqB#>?GrfNd%AYj3vlntDnhz!FqTZLhUE?ntz96fiF?Mg$f(V@IH)9uFi| zbGxFC_L<Q@mjqGm7dIp}MPGR(aT{QrbjiCtk_fmD<$8huB2USqA4w;)d^KNXHGTgKwz=o#|{Au~FcsEh3(eMFzoK%@;BQZ4t9teDkFr zKHIesZcpF)WPma4C!Yvn0-fRVlT##zJ^A4yI#(H%-y&izTyBTeG~a=!Elb!ytoGVt z5filK9y`~wdIKT=#bgt?7XA&FR=+hls^?_J(gABme$k*Po=4DA({-BieWKA|+jK;dlhcANx! zH*G!6{Y)=}L!{Y$hp7A;bmxQUb=YGcq-2}@feut*IFDuuZGtL(fG{A{XyM~~I(jyM zRxjse6*p+`I!8QRX8~Q;@P*(qYRJwBD z&LnYrzE~kL+bp#?#)UULKg*&~BcbZp95T;1_}W=piWj7))GJmT_fZ-M167>fSMyWa z*&^pXmFno(0tHKTQbgVKN>4w@h_kzwc{8Cp{8g9g5E{tQ!}U-hj!mRNfwBmkV?)Wm zKfZJvA}riO8B__fBU%4uZ@hDGuy>FlqV~dMytx)T=0M#{a1<}0U(bd%`pSqu^3x9r zr_#;5a6+GgV&Xwd%&!$9uHFh}wtz%C!^-O`iqO@^N>sTX45uR5hL3j!6Wc{*>}(Kj z4u69I7!z1D3K-Z(>{qc`2X%PFYF~;-a9iBY{@(L%-L`N+Xxb?2Ae=xi1xy8xa?(Na zHcQKJ*MXEGki%DF9_~WNi@k&0|E)JGzC;|`B72%O4E#Bi8gU!2CLDr|$6Zm-z{4cq zevg$T;2XJv5+qp%sNCke3Ocvly;e9Nm3UwUmzFMOIT%6!Q1kQV?bB?X7LodOph^nh z=NlI_QJ+N*RjE)gqRRj5^DNe5o}`lN^|)6ww-Lc6Z6^P~BB9*L#+sG(!c zV^=IMyJ{*u(%CEWLMN9)iRL*%${3qgGQ=D(pjH4WmSA!7^#qG=pH{+JX-{*gV$e{p z>k+XE&l#?0`K8tBXcmM6$|$M@GSGYKrd7|ACGQ+h8g==8P2+)NMapRe|G*GZdMccO zYO-bJQdS-V+QMOfFPzGxX-Pwq=f-ES7p0`nC=+EXrKYcXIif#N1NPudqx`eNQGaLp z=j@^Wje2y-Fjb*N$SU>WX#6ybacYlh$kGP2r#g;}x;`*bILbB1-VRWz-;lGC@?xk< zJ2|GUJlLR7<9*>aB>GG`R7FADBV!Y;^~A1juTNo1GM*}~5%aleg0_el@4r3xW(RSH zm#=oe`F2DrXX!)#_IBs(PFC)RI1LsUmZ@QCFo%Ytn=tpcVBi4*{|}FIyK?{l diff --git a/rdkafka/1.9.0/Linux-x86_64_8/include/rdkafka.h b/rdkafka/1.9.0/Linux-x86_64_8/include/rdkafka.h deleted file mode 100644 index 04ea7fc..0000000 --- a/rdkafka/1.9.0/Linux-x86_64_8/include/rdkafka.h +++ /dev/null @@ -1,8378 +0,0 @@ -/* - * librdkafka - Apache Kafka C library - * - * Copyright (c) 2012-2020 Magnus Edenhill - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file rdkafka.h - * @brief Apache Kafka C/C++ consumer and producer client library. - * - * rdkafka.h contains the public API for librdkafka. - * The API is documented in this file as comments prefixing the function, type, - * enum, define, etc. - * - * @sa For the C++ interface see rdkafkacpp.h - * - * @tableofcontents - */ - - -/* @cond NO_DOC */ -#ifndef _RDKAFKA_H_ -#define _RDKAFKA_H_ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#if 0 -} /* Restore indent */ -#endif -#endif - -#ifdef _WIN32 -#include -#ifndef WIN32_MEAN_AND_LEAN -#define WIN32_MEAN_AND_LEAN -#endif -#include /* for sockaddr, .. */ -#ifndef _SSIZE_T_DEFINED -#define _SSIZE_T_DEFINED -typedef SSIZE_T ssize_t; -#endif -#define RD_UNUSED -#define RD_INLINE __inline -#define RD_DEPRECATED __declspec(deprecated) -#define RD_FORMAT(...) -#undef RD_EXPORT -#ifdef LIBRDKAFKA_STATICLIB -#define RD_EXPORT -#else -#ifdef LIBRDKAFKA_EXPORTS -#define RD_EXPORT __declspec(dllexport) -#else -#define RD_EXPORT __declspec(dllimport) -#endif -#ifndef LIBRDKAFKA_TYPECHECKS -#define LIBRDKAFKA_TYPECHECKS 0 -#endif -#endif - -#else -#include /* for sockaddr, .. */ - -#define RD_UNUSED __attribute__((unused)) -#define RD_INLINE inline -#define RD_EXPORT -#define RD_DEPRECATED __attribute__((deprecated)) - -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) -#define RD_FORMAT(...) __attribute__((format(__VA_ARGS__))) -#else -#define RD_FORMAT(...) -#endif - -#ifndef LIBRDKAFKA_TYPECHECKS -#define LIBRDKAFKA_TYPECHECKS 1 -#endif -#endif - - -/** - * @brief Type-checking macros - * Compile-time checking that \p ARG is of type \p TYPE. - * @returns \p RET - */ -#if LIBRDKAFKA_TYPECHECKS -#define _LRK_TYPECHECK(RET, TYPE, ARG) \ - ({ \ - if (0) { \ - TYPE __t RD_UNUSED = (ARG); \ - } \ - RET; \ - }) - -#define _LRK_TYPECHECK2(RET, TYPE, ARG, TYPE2, ARG2) \ - ({ \ - if (0) { \ - TYPE __t RD_UNUSED = (ARG); \ - TYPE2 __t2 RD_UNUSED = (ARG2); \ - } \ - RET; \ - }) - -#define _LRK_TYPECHECK3(RET, TYPE, ARG, TYPE2, ARG2, TYPE3, ARG3) \ - ({ \ - if (0) { \ - TYPE __t RD_UNUSED = (ARG); \ - TYPE2 __t2 RD_UNUSED = (ARG2); \ - TYPE3 __t3 RD_UNUSED = (ARG3); \ - } \ - RET; \ - }) -#else -#define _LRK_TYPECHECK(RET, TYPE, ARG) (RET) -#define _LRK_TYPECHECK2(RET, TYPE, ARG, TYPE2, ARG2) (RET) -#define _LRK_TYPECHECK3(RET, TYPE, ARG, TYPE2, ARG2, TYPE3, ARG3) (RET) -#endif - -/* @endcond */ - - -/** - * @name librdkafka version - * @{ - * - * - */ - -/** - * @brief librdkafka version - * - * Interpreted as hex \c MM.mm.rr.xx: - * - MM = Major - * - mm = minor - * - rr = revision - * - xx = pre-release id (0xff is the final release) - * - * E.g.: \c 0x000801ff = 0.8.1 - * - * @remark This value should only be used during compile time, - * for runtime checks of version use rd_kafka_version() - */ -#define RD_KAFKA_VERSION 0x010900ff - -/** - * @brief Returns the librdkafka version as integer. - * - * @returns Version integer. - * - * @sa See RD_KAFKA_VERSION for how to parse the integer format. - * @sa Use rd_kafka_version_str() to retreive the version as a string. - */ -RD_EXPORT -int rd_kafka_version(void); - -/** - * @brief Returns the librdkafka version as string. - * - * @returns Version string - */ -RD_EXPORT -const char *rd_kafka_version_str(void); - -/**@}*/ - - -/** - * @name Constants, errors, types - * @{ - * - * - */ - - -/** - * @enum rd_kafka_type_t - * - * @brief rd_kafka_t handle type. - * - * @sa rd_kafka_new() - */ -typedef enum rd_kafka_type_t { - RD_KAFKA_PRODUCER, /**< Producer client */ - RD_KAFKA_CONSUMER /**< Consumer client */ -} rd_kafka_type_t; - - -/*! - * Timestamp types - * - * @sa rd_kafka_message_timestamp() - */ -typedef enum rd_kafka_timestamp_type_t { - RD_KAFKA_TIMESTAMP_NOT_AVAILABLE, /**< Timestamp not available */ - RD_KAFKA_TIMESTAMP_CREATE_TIME, /**< Message creation time */ - RD_KAFKA_TIMESTAMP_LOG_APPEND_TIME /**< Log append time */ -} rd_kafka_timestamp_type_t; - - - -/** - * @brief Retrieve supported debug contexts for use with the \c \"debug\" - * configuration property. (runtime) - * - * @returns Comma-separated list of available debugging contexts. - */ -RD_EXPORT -const char *rd_kafka_get_debug_contexts(void); - -/** - * @brief Supported debug contexts. (compile time) - * - * @deprecated This compile time value may be outdated at runtime due to - * linking another version of the library. - * Use rd_kafka_get_debug_contexts() instead. - */ -#define RD_KAFKA_DEBUG_CONTEXTS \ - "all,generic,broker,topic,metadata,feature,queue,msg,protocol,cgrp," \ - "security,fetch,interceptor,plugin,consumer,admin,eos,mock,assignor," \ - "conf" - - -/* @cond NO_DOC */ -/* Private types to provide ABI compatibility */ -typedef struct rd_kafka_s rd_kafka_t; -typedef struct rd_kafka_topic_s rd_kafka_topic_t; -typedef struct rd_kafka_conf_s rd_kafka_conf_t; -typedef struct rd_kafka_topic_conf_s rd_kafka_topic_conf_t; -typedef struct rd_kafka_queue_s rd_kafka_queue_t; -typedef struct rd_kafka_op_s rd_kafka_event_t; -typedef struct rd_kafka_topic_result_s rd_kafka_topic_result_t; -typedef struct rd_kafka_consumer_group_metadata_s - rd_kafka_consumer_group_metadata_t; -typedef struct rd_kafka_error_s rd_kafka_error_t; -typedef struct rd_kafka_headers_s rd_kafka_headers_t; -typedef struct rd_kafka_group_result_s rd_kafka_group_result_t; -typedef struct rd_kafka_acl_result_s rd_kafka_acl_result_t; -/* @endcond */ - - -/** - * @enum rd_kafka_resp_err_t - * @brief Error codes. - * - * The negative error codes delimited by two underscores - * (\c RD_KAFKA_RESP_ERR__..) denotes errors internal to librdkafka and are - * displayed as \c \"Local: \\", while the error codes - * delimited by a single underscore (\c RD_KAFKA_RESP_ERR_..) denote broker - * errors and are displayed as \c \"Broker: \\". - * - * @sa Use rd_kafka_err2str() to translate an error code a human readable string - */ -typedef enum { - /* Internal errors to rdkafka: */ - /** Begin internal error codes */ - RD_KAFKA_RESP_ERR__BEGIN = -200, - /** Received message is incorrect */ - RD_KAFKA_RESP_ERR__BAD_MSG = -199, - /** Bad/unknown compression */ - RD_KAFKA_RESP_ERR__BAD_COMPRESSION = -198, - /** Broker is going away */ - RD_KAFKA_RESP_ERR__DESTROY = -197, - /** Generic failure */ - RD_KAFKA_RESP_ERR__FAIL = -196, - /** Broker transport failure */ - RD_KAFKA_RESP_ERR__TRANSPORT = -195, - /** Critical system resource */ - RD_KAFKA_RESP_ERR__CRIT_SYS_RESOURCE = -194, - /** Failed to resolve broker */ - RD_KAFKA_RESP_ERR__RESOLVE = -193, - /** Produced message timed out*/ - RD_KAFKA_RESP_ERR__MSG_TIMED_OUT = -192, - /** Reached the end of the topic+partition queue on - * the broker. Not really an error. - * This event is disabled by default, - * see the `enable.partition.eof` configuration property. */ - RD_KAFKA_RESP_ERR__PARTITION_EOF = -191, - /** Permanent: Partition does not exist in cluster. */ - RD_KAFKA_RESP_ERR__UNKNOWN_PARTITION = -190, - /** File or filesystem error */ - RD_KAFKA_RESP_ERR__FS = -189, - /** Permanent: Topic does not exist in cluster. */ - RD_KAFKA_RESP_ERR__UNKNOWN_TOPIC = -188, - /** All broker connections are down. */ - RD_KAFKA_RESP_ERR__ALL_BROKERS_DOWN = -187, - /** Invalid argument, or invalid configuration */ - RD_KAFKA_RESP_ERR__INVALID_ARG = -186, - /** Operation timed out */ - RD_KAFKA_RESP_ERR__TIMED_OUT = -185, - /** Queue is full */ - RD_KAFKA_RESP_ERR__QUEUE_FULL = -184, - /** ISR count < required.acks */ - RD_KAFKA_RESP_ERR__ISR_INSUFF = -183, - /** Broker node update */ - RD_KAFKA_RESP_ERR__NODE_UPDATE = -182, - /** SSL error */ - RD_KAFKA_RESP_ERR__SSL = -181, - /** Waiting for coordinator to become available. */ - RD_KAFKA_RESP_ERR__WAIT_COORD = -180, - /** Unknown client group */ - RD_KAFKA_RESP_ERR__UNKNOWN_GROUP = -179, - /** Operation in progress */ - RD_KAFKA_RESP_ERR__IN_PROGRESS = -178, - /** Previous operation in progress, wait for it to finish. */ - RD_KAFKA_RESP_ERR__PREV_IN_PROGRESS = -177, - /** This operation would interfere with an existing subscription */ - RD_KAFKA_RESP_ERR__EXISTING_SUBSCRIPTION = -176, - /** Assigned partitions (rebalance_cb) */ - RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS = -175, - /** Revoked partitions (rebalance_cb) */ - RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS = -174, - /** Conflicting use */ - RD_KAFKA_RESP_ERR__CONFLICT = -173, - /** Wrong state */ - RD_KAFKA_RESP_ERR__STATE = -172, - /** Unknown protocol */ - RD_KAFKA_RESP_ERR__UNKNOWN_PROTOCOL = -171, - /** Not implemented */ - RD_KAFKA_RESP_ERR__NOT_IMPLEMENTED = -170, - /** Authentication failure*/ - RD_KAFKA_RESP_ERR__AUTHENTICATION = -169, - /** No stored offset */ - RD_KAFKA_RESP_ERR__NO_OFFSET = -168, - /** Outdated */ - RD_KAFKA_RESP_ERR__OUTDATED = -167, - /** Timed out in queue */ - RD_KAFKA_RESP_ERR__TIMED_OUT_QUEUE = -166, - /** Feature not supported by broker */ - RD_KAFKA_RESP_ERR__UNSUPPORTED_FEATURE = -165, - /** Awaiting cache update */ - RD_KAFKA_RESP_ERR__WAIT_CACHE = -164, - /** Operation interrupted (e.g., due to yield)) */ - RD_KAFKA_RESP_ERR__INTR = -163, - /** Key serialization error */ - RD_KAFKA_RESP_ERR__KEY_SERIALIZATION = -162, - /** Value serialization error */ - RD_KAFKA_RESP_ERR__VALUE_SERIALIZATION = -161, - /** Key deserialization error */ - RD_KAFKA_RESP_ERR__KEY_DESERIALIZATION = -160, - /** Value deserialization error */ - RD_KAFKA_RESP_ERR__VALUE_DESERIALIZATION = -159, - /** Partial response */ - RD_KAFKA_RESP_ERR__PARTIAL = -158, - /** Modification attempted on read-only object */ - RD_KAFKA_RESP_ERR__READ_ONLY = -157, - /** No such entry / item not found */ - RD_KAFKA_RESP_ERR__NOENT = -156, - /** Read underflow */ - RD_KAFKA_RESP_ERR__UNDERFLOW = -155, - /** Invalid type */ - RD_KAFKA_RESP_ERR__INVALID_TYPE = -154, - /** Retry operation */ - RD_KAFKA_RESP_ERR__RETRY = -153, - /** Purged in queue */ - RD_KAFKA_RESP_ERR__PURGE_QUEUE = -152, - /** Purged in flight */ - RD_KAFKA_RESP_ERR__PURGE_INFLIGHT = -151, - /** Fatal error: see rd_kafka_fatal_error() */ - RD_KAFKA_RESP_ERR__FATAL = -150, - /** Inconsistent state */ - RD_KAFKA_RESP_ERR__INCONSISTENT = -149, - /** Gap-less ordering would not be guaranteed if proceeding */ - RD_KAFKA_RESP_ERR__GAPLESS_GUARANTEE = -148, - /** Maximum poll interval exceeded */ - RD_KAFKA_RESP_ERR__MAX_POLL_EXCEEDED = -147, - /** Unknown broker */ - RD_KAFKA_RESP_ERR__UNKNOWN_BROKER = -146, - /** Functionality not configured */ - RD_KAFKA_RESP_ERR__NOT_CONFIGURED = -145, - /** Instance has been fenced */ - RD_KAFKA_RESP_ERR__FENCED = -144, - /** Application generated error */ - RD_KAFKA_RESP_ERR__APPLICATION = -143, - /** Assignment lost */ - RD_KAFKA_RESP_ERR__ASSIGNMENT_LOST = -142, - /** No operation performed */ - RD_KAFKA_RESP_ERR__NOOP = -141, - /** No offset to automatically reset to */ - RD_KAFKA_RESP_ERR__AUTO_OFFSET_RESET = -140, - - /** End internal error codes */ - RD_KAFKA_RESP_ERR__END = -100, - - /* Kafka broker errors: */ - /** Unknown broker error */ - RD_KAFKA_RESP_ERR_UNKNOWN = -1, - /** Success */ - RD_KAFKA_RESP_ERR_NO_ERROR = 0, - /** Offset out of range */ - RD_KAFKA_RESP_ERR_OFFSET_OUT_OF_RANGE = 1, - /** Invalid message */ - RD_KAFKA_RESP_ERR_INVALID_MSG = 2, - /** Unknown topic or partition */ - RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART = 3, - /** Invalid message size */ - RD_KAFKA_RESP_ERR_INVALID_MSG_SIZE = 4, - /** Leader not available */ - RD_KAFKA_RESP_ERR_LEADER_NOT_AVAILABLE = 5, - /** Not leader for partition */ - RD_KAFKA_RESP_ERR_NOT_LEADER_FOR_PARTITION = 6, - /** Request timed out */ - RD_KAFKA_RESP_ERR_REQUEST_TIMED_OUT = 7, - /** Broker not available */ - RD_KAFKA_RESP_ERR_BROKER_NOT_AVAILABLE = 8, - /** Replica not available */ - RD_KAFKA_RESP_ERR_REPLICA_NOT_AVAILABLE = 9, - /** Message size too large */ - RD_KAFKA_RESP_ERR_MSG_SIZE_TOO_LARGE = 10, - /** StaleControllerEpochCode */ - RD_KAFKA_RESP_ERR_STALE_CTRL_EPOCH = 11, - /** Offset metadata string too large */ - RD_KAFKA_RESP_ERR_OFFSET_METADATA_TOO_LARGE = 12, - /** Broker disconnected before response received */ - RD_KAFKA_RESP_ERR_NETWORK_EXCEPTION = 13, - /** Coordinator load in progress */ - RD_KAFKA_RESP_ERR_COORDINATOR_LOAD_IN_PROGRESS = 14, -/** Group coordinator load in progress */ -#define RD_KAFKA_RESP_ERR_GROUP_LOAD_IN_PROGRESS \ - RD_KAFKA_RESP_ERR_COORDINATOR_LOAD_IN_PROGRESS - /** Coordinator not available */ - RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE = 15, -/** Group coordinator not available */ -#define RD_KAFKA_RESP_ERR_GROUP_COORDINATOR_NOT_AVAILABLE \ - RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE - /** Not coordinator */ - RD_KAFKA_RESP_ERR_NOT_COORDINATOR = 16, -/** Not coordinator for group */ -#define RD_KAFKA_RESP_ERR_NOT_COORDINATOR_FOR_GROUP \ - RD_KAFKA_RESP_ERR_NOT_COORDINATOR - /** Invalid topic */ - RD_KAFKA_RESP_ERR_TOPIC_EXCEPTION = 17, - /** Message batch larger than configured server segment size */ - RD_KAFKA_RESP_ERR_RECORD_LIST_TOO_LARGE = 18, - /** Not enough in-sync replicas */ - RD_KAFKA_RESP_ERR_NOT_ENOUGH_REPLICAS = 19, - /** Message(s) written to insufficient number of in-sync replicas */ - RD_KAFKA_RESP_ERR_NOT_ENOUGH_REPLICAS_AFTER_APPEND = 20, - /** Invalid required acks value */ - RD_KAFKA_RESP_ERR_INVALID_REQUIRED_ACKS = 21, - /** Specified group generation id is not valid */ - RD_KAFKA_RESP_ERR_ILLEGAL_GENERATION = 22, - /** Inconsistent group protocol */ - RD_KAFKA_RESP_ERR_INCONSISTENT_GROUP_PROTOCOL = 23, - /** Invalid group.id */ - RD_KAFKA_RESP_ERR_INVALID_GROUP_ID = 24, - /** Unknown member */ - RD_KAFKA_RESP_ERR_UNKNOWN_MEMBER_ID = 25, - /** Invalid session timeout */ - RD_KAFKA_RESP_ERR_INVALID_SESSION_TIMEOUT = 26, - /** Group rebalance in progress */ - RD_KAFKA_RESP_ERR_REBALANCE_IN_PROGRESS = 27, - /** Commit offset data size is not valid */ - RD_KAFKA_RESP_ERR_INVALID_COMMIT_OFFSET_SIZE = 28, - /** Topic authorization failed */ - RD_KAFKA_RESP_ERR_TOPIC_AUTHORIZATION_FAILED = 29, - /** Group authorization failed */ - RD_KAFKA_RESP_ERR_GROUP_AUTHORIZATION_FAILED = 30, - /** Cluster authorization failed */ - RD_KAFKA_RESP_ERR_CLUSTER_AUTHORIZATION_FAILED = 31, - /** Invalid timestamp */ - RD_KAFKA_RESP_ERR_INVALID_TIMESTAMP = 32, - /** Unsupported SASL mechanism */ - RD_KAFKA_RESP_ERR_UNSUPPORTED_SASL_MECHANISM = 33, - /** Illegal SASL state */ - RD_KAFKA_RESP_ERR_ILLEGAL_SASL_STATE = 34, - /** Unuspported version */ - RD_KAFKA_RESP_ERR_UNSUPPORTED_VERSION = 35, - /** Topic already exists */ - RD_KAFKA_RESP_ERR_TOPIC_ALREADY_EXISTS = 36, - /** Invalid number of partitions */ - RD_KAFKA_RESP_ERR_INVALID_PARTITIONS = 37, - /** Invalid replication factor */ - RD_KAFKA_RESP_ERR_INVALID_REPLICATION_FACTOR = 38, - /** Invalid replica assignment */ - RD_KAFKA_RESP_ERR_INVALID_REPLICA_ASSIGNMENT = 39, - /** Invalid config */ - RD_KAFKA_RESP_ERR_INVALID_CONFIG = 40, - /** Not controller for cluster */ - RD_KAFKA_RESP_ERR_NOT_CONTROLLER = 41, - /** Invalid request */ - RD_KAFKA_RESP_ERR_INVALID_REQUEST = 42, - /** Message format on broker does not support request */ - RD_KAFKA_RESP_ERR_UNSUPPORTED_FOR_MESSAGE_FORMAT = 43, - /** Policy violation */ - RD_KAFKA_RESP_ERR_POLICY_VIOLATION = 44, - /** Broker received an out of order sequence number */ - RD_KAFKA_RESP_ERR_OUT_OF_ORDER_SEQUENCE_NUMBER = 45, - /** Broker received a duplicate sequence number */ - RD_KAFKA_RESP_ERR_DUPLICATE_SEQUENCE_NUMBER = 46, - /** Producer attempted an operation with an old epoch */ - RD_KAFKA_RESP_ERR_INVALID_PRODUCER_EPOCH = 47, - /** Producer attempted a transactional operation in an invalid state */ - RD_KAFKA_RESP_ERR_INVALID_TXN_STATE = 48, - /** Producer attempted to use a producer id which is not - * currently assigned to its transactional id */ - RD_KAFKA_RESP_ERR_INVALID_PRODUCER_ID_MAPPING = 49, - /** Transaction timeout is larger than the maximum - * value allowed by the broker's max.transaction.timeout.ms */ - RD_KAFKA_RESP_ERR_INVALID_TRANSACTION_TIMEOUT = 50, - /** Producer attempted to update a transaction while another - * concurrent operation on the same transaction was ongoing */ - RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS = 51, - /** Indicates that the transaction coordinator sending a - * WriteTxnMarker is no longer the current coordinator for a - * given producer */ - RD_KAFKA_RESP_ERR_TRANSACTION_COORDINATOR_FENCED = 52, - /** Transactional Id authorization failed */ - RD_KAFKA_RESP_ERR_TRANSACTIONAL_ID_AUTHORIZATION_FAILED = 53, - /** Security features are disabled */ - RD_KAFKA_RESP_ERR_SECURITY_DISABLED = 54, - /** Operation not attempted */ - RD_KAFKA_RESP_ERR_OPERATION_NOT_ATTEMPTED = 55, - /** Disk error when trying to access log file on the disk */ - RD_KAFKA_RESP_ERR_KAFKA_STORAGE_ERROR = 56, - /** The user-specified log directory is not found in the broker config - */ - RD_KAFKA_RESP_ERR_LOG_DIR_NOT_FOUND = 57, - /** SASL Authentication failed */ - RD_KAFKA_RESP_ERR_SASL_AUTHENTICATION_FAILED = 58, - /** Unknown Producer Id */ - RD_KAFKA_RESP_ERR_UNKNOWN_PRODUCER_ID = 59, - /** Partition reassignment is in progress */ - RD_KAFKA_RESP_ERR_REASSIGNMENT_IN_PROGRESS = 60, - /** Delegation Token feature is not enabled */ - RD_KAFKA_RESP_ERR_DELEGATION_TOKEN_AUTH_DISABLED = 61, - /** Delegation Token is not found on server */ - RD_KAFKA_RESP_ERR_DELEGATION_TOKEN_NOT_FOUND = 62, - /** Specified Principal is not valid Owner/Renewer */ - RD_KAFKA_RESP_ERR_DELEGATION_TOKEN_OWNER_MISMATCH = 63, - /** Delegation Token requests are not allowed on this connection */ - RD_KAFKA_RESP_ERR_DELEGATION_TOKEN_REQUEST_NOT_ALLOWED = 64, - /** Delegation Token authorization failed */ - RD_KAFKA_RESP_ERR_DELEGATION_TOKEN_AUTHORIZATION_FAILED = 65, - /** Delegation Token is expired */ - RD_KAFKA_RESP_ERR_DELEGATION_TOKEN_EXPIRED = 66, - /** Supplied principalType is not supported */ - RD_KAFKA_RESP_ERR_INVALID_PRINCIPAL_TYPE = 67, - /** The group is not empty */ - RD_KAFKA_RESP_ERR_NON_EMPTY_GROUP = 68, - /** The group id does not exist */ - RD_KAFKA_RESP_ERR_GROUP_ID_NOT_FOUND = 69, - /** The fetch session ID was not found */ - RD_KAFKA_RESP_ERR_FETCH_SESSION_ID_NOT_FOUND = 70, - /** The fetch session epoch is invalid */ - RD_KAFKA_RESP_ERR_INVALID_FETCH_SESSION_EPOCH = 71, - /** No matching listener */ - RD_KAFKA_RESP_ERR_LISTENER_NOT_FOUND = 72, - /** Topic deletion is disabled */ - RD_KAFKA_RESP_ERR_TOPIC_DELETION_DISABLED = 73, - /** Leader epoch is older than broker epoch */ - RD_KAFKA_RESP_ERR_FENCED_LEADER_EPOCH = 74, - /** Leader epoch is newer than broker epoch */ - RD_KAFKA_RESP_ERR_UNKNOWN_LEADER_EPOCH = 75, - /** Unsupported compression type */ - RD_KAFKA_RESP_ERR_UNSUPPORTED_COMPRESSION_TYPE = 76, - /** Broker epoch has changed */ - RD_KAFKA_RESP_ERR_STALE_BROKER_EPOCH = 77, - /** Leader high watermark is not caught up */ - RD_KAFKA_RESP_ERR_OFFSET_NOT_AVAILABLE = 78, - /** Group member needs a valid member ID */ - RD_KAFKA_RESP_ERR_MEMBER_ID_REQUIRED = 79, - /** Preferred leader was not available */ - RD_KAFKA_RESP_ERR_PREFERRED_LEADER_NOT_AVAILABLE = 80, - /** Consumer group has reached maximum size */ - RD_KAFKA_RESP_ERR_GROUP_MAX_SIZE_REACHED = 81, - /** Static consumer fenced by other consumer with same - * group.instance.id. */ - RD_KAFKA_RESP_ERR_FENCED_INSTANCE_ID = 82, - /** Eligible partition leaders are not available */ - RD_KAFKA_RESP_ERR_ELIGIBLE_LEADERS_NOT_AVAILABLE = 83, - /** Leader election not needed for topic partition */ - RD_KAFKA_RESP_ERR_ELECTION_NOT_NEEDED = 84, - /** No partition reassignment is in progress */ - RD_KAFKA_RESP_ERR_NO_REASSIGNMENT_IN_PROGRESS = 85, - /** Deleting offsets of a topic while the consumer group is - * subscribed to it */ - RD_KAFKA_RESP_ERR_GROUP_SUBSCRIBED_TO_TOPIC = 86, - /** Broker failed to validate record */ - RD_KAFKA_RESP_ERR_INVALID_RECORD = 87, - /** There are unstable offsets that need to be cleared */ - RD_KAFKA_RESP_ERR_UNSTABLE_OFFSET_COMMIT = 88, - /** Throttling quota has been exceeded */ - RD_KAFKA_RESP_ERR_THROTTLING_QUOTA_EXCEEDED = 89, - /** There is a newer producer with the same transactionalId - * which fences the current one */ - RD_KAFKA_RESP_ERR_PRODUCER_FENCED = 90, - /** Request illegally referred to resource that does not exist */ - RD_KAFKA_RESP_ERR_RESOURCE_NOT_FOUND = 91, - /** Request illegally referred to the same resource twice */ - RD_KAFKA_RESP_ERR_DUPLICATE_RESOURCE = 92, - /** Requested credential would not meet criteria for acceptability */ - RD_KAFKA_RESP_ERR_UNACCEPTABLE_CREDENTIAL = 93, - /** Indicates that the either the sender or recipient of a - * voter-only request is not one of the expected voters */ - RD_KAFKA_RESP_ERR_INCONSISTENT_VOTER_SET = 94, - /** Invalid update version */ - RD_KAFKA_RESP_ERR_INVALID_UPDATE_VERSION = 95, - /** Unable to update finalized features due to server error */ - RD_KAFKA_RESP_ERR_FEATURE_UPDATE_FAILED = 96, - /** Request principal deserialization failed during forwarding */ - RD_KAFKA_RESP_ERR_PRINCIPAL_DESERIALIZATION_FAILURE = 97, - - RD_KAFKA_RESP_ERR_END_ALL, -} rd_kafka_resp_err_t; - - -/** - * @brief Error code value, name and description. - * Typically for use with language bindings to automatically expose - * the full set of librdkafka error codes. - */ -struct rd_kafka_err_desc { - rd_kafka_resp_err_t code; /**< Error code */ - const char *name; /**< Error name, same as code enum sans prefix */ - const char *desc; /**< Human readable error description. */ -}; - - -/** - * @brief Returns the full list of error codes. - */ -RD_EXPORT -void rd_kafka_get_err_descs(const struct rd_kafka_err_desc **errdescs, - size_t *cntp); - - - -/** - * @brief Returns a human readable representation of a kafka error. - * - * @param err Error code to translate - */ -RD_EXPORT -const char *rd_kafka_err2str(rd_kafka_resp_err_t err); - - - -/** - * @brief Returns the error code name (enum name). - * - * @param err Error code to translate - */ -RD_EXPORT -const char *rd_kafka_err2name(rd_kafka_resp_err_t err); - - -/** - * @brief Returns the last error code generated by a legacy API call - * in the current thread. - * - * The legacy APIs are the ones using errno to propagate error value, namely: - * - rd_kafka_topic_new() - * - rd_kafka_consume_start() - * - rd_kafka_consume_stop() - * - rd_kafka_consume() - * - rd_kafka_consume_batch() - * - rd_kafka_consume_callback() - * - rd_kafka_consume_queue() - * - rd_kafka_produce() - * - * The main use for this function is to avoid converting system \p errno - * values to rd_kafka_resp_err_t codes for legacy APIs. - * - * @remark The last error is stored per-thread, if multiple rd_kafka_t handles - * are used in the same application thread the developer needs to - * make sure rd_kafka_last_error() is called immediately after - * a failed API call. - * - * @remark errno propagation from librdkafka is not safe on Windows - * and should not be used, use rd_kafka_last_error() instead. - */ -RD_EXPORT -rd_kafka_resp_err_t rd_kafka_last_error(void); - - -/** - * @brief Converts the system errno value \p errnox to a rd_kafka_resp_err_t - * error code upon failure from the following functions: - * - rd_kafka_topic_new() - * - rd_kafka_consume_start() - * - rd_kafka_consume_stop() - * - rd_kafka_consume() - * - rd_kafka_consume_batch() - * - rd_kafka_consume_callback() - * - rd_kafka_consume_queue() - * - rd_kafka_produce() - * - * @param errnox System errno value to convert - * - * @returns Appropriate error code for \p errnox - * - * @remark A better alternative is to call rd_kafka_last_error() immediately - * after any of the above functions return -1 or NULL. - * - * @deprecated Use rd_kafka_last_error() to retrieve the last error code - * set by the legacy librdkafka APIs. - * - * @sa rd_kafka_last_error() - */ -RD_EXPORT RD_DEPRECATED rd_kafka_resp_err_t rd_kafka_errno2err(int errnox); - - -/** - * @brief Returns the thread-local system errno - * - * On most platforms this is the same as \p errno but in case of different - * runtimes between library and application (e.g., Windows static DLLs) - * this provides a means for exposing the errno librdkafka uses. - * - * @remark The value is local to the current calling thread. - * - * @deprecated Use rd_kafka_last_error() to retrieve the last error code - * set by the legacy librdkafka APIs. - */ -RD_EXPORT RD_DEPRECATED int rd_kafka_errno(void); - - - -/** - * @brief Returns the first fatal error set on this client instance, - * or RD_KAFKA_RESP_ERR_NO_ERROR if no fatal error has occurred. - * - * This function is to be used with the Idempotent Producer and \c error_cb - * to detect fatal errors. - * - * Generally all errors raised by \c error_cb are to be considered - * informational and temporary, the client will try to recover from all - * errors in a graceful fashion (by retrying, etc). - * - * However, some errors should logically be considered fatal to retain - * consistency; in particular a set of errors that may occur when using the - * Idempotent Producer and the in-order or exactly-once producer guarantees - * can't be satisfied. - * - * @param rk Client instance. - * @param errstr A human readable error string (nul-terminated) is written to - * this location that must be of at least \p errstr_size bytes. - * The \p errstr is only written to if there is a fatal error. - * @param errstr_size Writable size in \p errstr. - * - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR if no fatal error has been raised, else - * any other error code. - */ -RD_EXPORT -rd_kafka_resp_err_t -rd_kafka_fatal_error(rd_kafka_t *rk, char *errstr, size_t errstr_size); - - -/** - * @brief Trigger a fatal error for testing purposes. - * - * Since there is no practical way to trigger real fatal errors in the - * idempotent producer, this method allows an application to trigger - * fabricated fatal errors in tests to check its error handling code. - * - * @param rk Client instance. - * @param err The underlying error code. - * @param reason A human readable error reason. - * Will be prefixed with "test_fatal_error: " to differentiate - * from real fatal errors. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR if a fatal error was triggered, or - * RD_KAFKA_RESP_ERR__PREV_IN_PROGRESS if a previous fatal error - * has already been triggered. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_test_fatal_error(rd_kafka_t *rk, - rd_kafka_resp_err_t err, - const char *reason); - - -/** - * @returns the error code for \p error or RD_KAFKA_RESP_ERR_NO_ERROR if - * \p error is NULL. - */ -RD_EXPORT -rd_kafka_resp_err_t rd_kafka_error_code(const rd_kafka_error_t *error); - -/** - * @returns the error code name for \p error, e.g, "ERR_UNKNOWN_MEMBER_ID", - * or an empty string if \p error is NULL. - * - * @remark The lifetime of the returned pointer is the same as the error object. - * - * @sa rd_kafka_err2name() - */ -RD_EXPORT -const char *rd_kafka_error_name(const rd_kafka_error_t *error); - -/** - * @returns a human readable error string for \p error, - * or an empty string if \p error is NULL. - * - * @remark The lifetime of the returned pointer is the same as the error object. - */ -RD_EXPORT -const char *rd_kafka_error_string(const rd_kafka_error_t *error); - - -/** - * @returns 1 if the error is a fatal error, indicating that the client - * instance is no longer usable, else 0 (also if \p error is NULL). - */ -RD_EXPORT -int rd_kafka_error_is_fatal(const rd_kafka_error_t *error); - - -/** - * @returns 1 if the operation may be retried, - * else 0 (also if \p error is NULL). - */ -RD_EXPORT -int rd_kafka_error_is_retriable(const rd_kafka_error_t *error); - - -/** - * @returns 1 if the error is an abortable transaction error in which case - * the application must call rd_kafka_abort_transaction() and - * start a new transaction with rd_kafka_begin_transaction() if it - * wishes to proceed with transactions. - * Else returns 0 (also if \p error is NULL). - * - * @remark The return value of this method is only valid for errors returned - * by the transactional API. - */ -RD_EXPORT -int rd_kafka_error_txn_requires_abort(const rd_kafka_error_t *error); - -/** - * @brief Free and destroy an error object. - * - * @remark As a conveniance it is permitted to pass a NULL \p error. - */ -RD_EXPORT -void rd_kafka_error_destroy(rd_kafka_error_t *error); - - -/** - * @brief Create a new error object with error \p code and optional - * human readable error string in \p fmt. - * - * This method is mainly to be used for mocking errors in application test code. - * - * The returned object must be destroyed with rd_kafka_error_destroy(). - */ -RD_EXPORT -rd_kafka_error_t *rd_kafka_error_new(rd_kafka_resp_err_t code, - const char *fmt, - ...) RD_FORMAT(printf, 2, 3); - - -/** - * @brief Topic+Partition place holder - * - * Generic place holder for a Topic+Partition and its related information - * used for multiple purposes: - * - consumer offset (see rd_kafka_commit(), et.al.) - * - group rebalancing callback (rd_kafka_conf_set_rebalance_cb()) - * - offset commit result callback (rd_kafka_conf_set_offset_commit_cb()) - */ - -/** - * @brief Generic place holder for a specific Topic+Partition. - * - * @sa rd_kafka_topic_partition_list_new() - */ -typedef struct rd_kafka_topic_partition_s { - char *topic; /**< Topic name */ - int32_t partition; /**< Partition */ - int64_t offset; /**< Offset */ - void *metadata; /**< Metadata */ - size_t metadata_size; /**< Metadata size */ - void *opaque; /**< Opaque value for application use */ - rd_kafka_resp_err_t err; /**< Error code, depending on use. */ - void *_private; /**< INTERNAL USE ONLY, - * INITIALIZE TO ZERO, DO NOT TOUCH */ -} rd_kafka_topic_partition_t; - - -/** - * @brief Destroy a rd_kafka_topic_partition_t. - * @remark This must not be called for elements in a topic partition list. - */ -RD_EXPORT -void rd_kafka_topic_partition_destroy(rd_kafka_topic_partition_t *rktpar); - - -/** - * @brief A growable list of Topic+Partitions. - * - */ -typedef struct rd_kafka_topic_partition_list_s { - int cnt; /**< Current number of elements */ - int size; /**< Current allocated size */ - rd_kafka_topic_partition_t *elems; /**< Element array[] */ -} rd_kafka_topic_partition_list_t; - - -/** - * @brief Create a new list/vector Topic+Partition container. - * - * @param size Initial allocated size used when the expected number of - * elements is known or can be estimated. - * Avoids reallocation and possibly relocation of the - * elems array. - * - * @returns A newly allocated Topic+Partition list. - * - * @remark Use rd_kafka_topic_partition_list_destroy() to free all resources - * in use by a list and the list itself. - * @sa rd_kafka_topic_partition_list_add() - */ -RD_EXPORT -rd_kafka_topic_partition_list_t *rd_kafka_topic_partition_list_new(int size); - - -/** - * @brief Free all resources used by the list and the list itself. - */ -RD_EXPORT -void rd_kafka_topic_partition_list_destroy( - rd_kafka_topic_partition_list_t *rkparlist); - -/** - * @brief Add topic+partition to list - * - * @param rktparlist List to extend - * @param topic Topic name (copied) - * @param partition Partition id - * - * @returns The object which can be used to fill in additionals fields. - */ -RD_EXPORT -rd_kafka_topic_partition_t * -rd_kafka_topic_partition_list_add(rd_kafka_topic_partition_list_t *rktparlist, - const char *topic, - int32_t partition); - - -/** - * @brief Add range of partitions from \p start to \p stop inclusive. - * - * @param rktparlist List to extend - * @param topic Topic name (copied) - * @param start Start partition of range - * @param stop Last partition of range (inclusive) - */ -RD_EXPORT -void rd_kafka_topic_partition_list_add_range( - rd_kafka_topic_partition_list_t *rktparlist, - const char *topic, - int32_t start, - int32_t stop); - - - -/** - * @brief Delete partition from list. - * - * @param rktparlist List to modify - * @param topic Topic name to match - * @param partition Partition to match - * - * @returns 1 if partition was found (and removed), else 0. - * - * @remark Any held indices to elems[] are unusable after this call returns 1. - */ -RD_EXPORT -int rd_kafka_topic_partition_list_del( - rd_kafka_topic_partition_list_t *rktparlist, - const char *topic, - int32_t partition); - - -/** - * @brief Delete partition from list by elems[] index. - * - * @returns 1 if partition was found (and removed), else 0. - * - * @sa rd_kafka_topic_partition_list_del() - */ -RD_EXPORT -int rd_kafka_topic_partition_list_del_by_idx( - rd_kafka_topic_partition_list_t *rktparlist, - int idx); - - -/** - * @brief Make a copy of an existing list. - * - * @param src The existing list to copy. - * - * @returns A new list fully populated to be identical to \p src - */ -RD_EXPORT -rd_kafka_topic_partition_list_t * -rd_kafka_topic_partition_list_copy(const rd_kafka_topic_partition_list_t *src); - - - -/** - * @brief Set offset to \p offset for \p topic and \p partition - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or - * RD_KAFKA_RESP_ERR__UNKNOWN_PARTITION if \p partition was not found - * in the list. - */ -RD_EXPORT -rd_kafka_resp_err_t rd_kafka_topic_partition_list_set_offset( - rd_kafka_topic_partition_list_t *rktparlist, - const char *topic, - int32_t partition, - int64_t offset); - - - -/** - * @brief Find element by \p topic and \p partition. - * - * @returns a pointer to the first matching element, or NULL if not found. - */ -RD_EXPORT -rd_kafka_topic_partition_t *rd_kafka_topic_partition_list_find( - const rd_kafka_topic_partition_list_t *rktparlist, - const char *topic, - int32_t partition); - - -/** - * @brief Sort list using comparator \p cmp. - * - * If \p cmp is NULL the default comparator will be used that - * sorts by ascending topic name and partition. - * - * \p cmp_opaque is provided as the \p cmp_opaque argument to \p cmp. - * - */ -RD_EXPORT void rd_kafka_topic_partition_list_sort( - rd_kafka_topic_partition_list_t *rktparlist, - int (*cmp)(const void *a, const void *b, void *cmp_opaque), - void *cmp_opaque); - - -/**@}*/ - - - -/** - * @name Var-arg tag types - * @{ - * - */ - -/** - * @enum rd_kafka_vtype_t - * - * @brief Var-arg tag types - * - * @sa rd_kafka_producev() - */ -typedef enum rd_kafka_vtype_t { - RD_KAFKA_VTYPE_END, /**< va-arg sentinel */ - RD_KAFKA_VTYPE_TOPIC, /**< (const char *) Topic name */ - RD_KAFKA_VTYPE_RKT, /**< (rd_kafka_topic_t *) Topic handle */ - RD_KAFKA_VTYPE_PARTITION, /**< (int32_t) Partition */ - RD_KAFKA_VTYPE_VALUE, /**< (void *, size_t) Message value (payload)*/ - RD_KAFKA_VTYPE_KEY, /**< (void *, size_t) Message key */ - RD_KAFKA_VTYPE_OPAQUE, /**< (void *) Per-message application opaque - * value. This is the same as - * the _private field in - * rd_kafka_message_t, also known - * as the msg_opaque. */ - RD_KAFKA_VTYPE_MSGFLAGS, /**< (int) RD_KAFKA_MSG_F_.. flags */ - RD_KAFKA_VTYPE_TIMESTAMP, /**< (int64_t) Milliseconds since epoch UTC */ - RD_KAFKA_VTYPE_HEADER, /**< (const char *, const void *, ssize_t) - * Message Header */ - RD_KAFKA_VTYPE_HEADERS, /**< (rd_kafka_headers_t *) Headers list */ -} rd_kafka_vtype_t; - - -/** - * @brief VTYPE + argument container for use with rd_kafka_produce_va() - * - * See RD_KAFKA_V_..() macros below for which union field corresponds - * to which RD_KAFKA_VTYPE_... - */ -typedef struct rd_kafka_vu_s { - rd_kafka_vtype_t vtype; /**< RD_KAFKA_VTYPE_.. */ - /** Value union, see RD_KAFKA_V_.. macros for which field to use. */ - union { - const char *cstr; - rd_kafka_topic_t *rkt; - int i; - int32_t i32; - int64_t i64; - struct { - void *ptr; - size_t size; - } mem; - struct { - const char *name; - const void *val; - ssize_t size; - } header; - rd_kafka_headers_t *headers; - void *ptr; - char _pad[64]; /**< Padding size for future-proofness */ - } u; -} rd_kafka_vu_t; - -/** - * @brief Convenience macros for rd_kafka_vtype_t that takes the - * correct arguments for each vtype. - */ - -/*! - * va-arg end sentinel used to terminate the variable argument list - */ -#define RD_KAFKA_V_END RD_KAFKA_VTYPE_END - -/*! - * Topic name (const char *) - * - * rd_kafka_vu_t field: u.cstr - */ -#define RD_KAFKA_V_TOPIC(topic) \ - _LRK_TYPECHECK(RD_KAFKA_VTYPE_TOPIC, const char *, topic), \ - (const char *)topic -/*! - * Topic object (rd_kafka_topic_t *) - * - * rd_kafka_vu_t field: u.rkt - */ -#define RD_KAFKA_V_RKT(rkt) \ - _LRK_TYPECHECK(RD_KAFKA_VTYPE_RKT, rd_kafka_topic_t *, rkt), \ - (rd_kafka_topic_t *)rkt -/*! - * Partition (int32_t) - * - * rd_kafka_vu_t field: u.i32 - */ -#define RD_KAFKA_V_PARTITION(partition) \ - _LRK_TYPECHECK(RD_KAFKA_VTYPE_PARTITION, int32_t, partition), \ - (int32_t)partition -/*! - * Message value/payload pointer and length (void *, size_t) - * - * rd_kafka_vu_t fields: u.mem.ptr, u.mem.size - */ -#define RD_KAFKA_V_VALUE(VALUE, LEN) \ - _LRK_TYPECHECK2(RD_KAFKA_VTYPE_VALUE, void *, VALUE, size_t, LEN), \ - (void *)VALUE, (size_t)LEN -/*! - * Message key pointer and length (const void *, size_t) - * - * rd_kafka_vu_t field: u.mem.ptr, rd_kafka_vu.t.u.mem.size - */ -#define RD_KAFKA_V_KEY(KEY, LEN) \ - _LRK_TYPECHECK2(RD_KAFKA_VTYPE_KEY, const void *, KEY, size_t, LEN), \ - (void *)KEY, (size_t)LEN -/*! - * Message opaque pointer (void *) - * Same as \c msg_opaque, \c produce(.., msg_opaque), - * and \c rkmessage->_private . - * - * rd_kafka_vu_t field: u.ptr - */ -#define RD_KAFKA_V_OPAQUE(msg_opaque) \ - _LRK_TYPECHECK(RD_KAFKA_VTYPE_OPAQUE, void *, msg_opaque), \ - (void *)msg_opaque -/*! - * Message flags (int) - * @sa RD_KAFKA_MSG_F_COPY, et.al. - * - * rd_kafka_vu_t field: u.i - */ -#define RD_KAFKA_V_MSGFLAGS(msgflags) \ - _LRK_TYPECHECK(RD_KAFKA_VTYPE_MSGFLAGS, int, msgflags), (int)msgflags -/*! - * Timestamp in milliseconds since epoch UTC (int64_t). - * A value of 0 will use the current wall-clock time. - * - * rd_kafka_vu_t field: u.i64 - */ -#define RD_KAFKA_V_TIMESTAMP(timestamp) \ - _LRK_TYPECHECK(RD_KAFKA_VTYPE_TIMESTAMP, int64_t, timestamp), \ - (int64_t)timestamp -/*! - * Add Message Header (const char *NAME, const void *VALUE, ssize_t LEN). - * @sa rd_kafka_header_add() - * @remark RD_KAFKA_V_HEADER() and RD_KAFKA_V_HEADERS() MUST NOT be mixed - * in the same call to producev(). - * - * rd_kafka_vu_t fields: u.header.name, u.header.val, u.header.size - */ -#define RD_KAFKA_V_HEADER(NAME, VALUE, LEN) \ - _LRK_TYPECHECK3(RD_KAFKA_VTYPE_HEADER, const char *, NAME, \ - const void *, VALUE, ssize_t, LEN), \ - (const char *)NAME, (const void *)VALUE, (ssize_t)LEN - -/*! - * Message Headers list (rd_kafka_headers_t *). - * The message object will assume ownership of the headers (unless producev() - * fails). - * Any existing headers will be replaced. - * @sa rd_kafka_message_set_headers() - * @remark RD_KAFKA_V_HEADER() and RD_KAFKA_V_HEADERS() MUST NOT be mixed - * in the same call to producev(). - * - * rd_kafka_vu_t fields: u.headers - */ -#define RD_KAFKA_V_HEADERS(HDRS) \ - _LRK_TYPECHECK(RD_KAFKA_VTYPE_HEADERS, rd_kafka_headers_t *, HDRS), \ - (rd_kafka_headers_t *)HDRS - - -/**@}*/ - - -/** - * @name Message headers - * @{ - * - * @brief Message headers consist of a list of (string key, binary value) pairs. - * Duplicate keys are supported and the order in which keys were - * added are retained. - * - * Header values are considered binary and may have three types of - * value: - * - proper value with size > 0 and a valid pointer - * - empty value with size = 0 and any non-NULL pointer - * - null value with size = 0 and a NULL pointer - * - * Headers require Apache Kafka broker version v0.11.0.0 or later. - * - * Header operations are O(n). - */ - - -/** - * @brief Create a new headers list. - * - * @param initial_count Preallocate space for this number of headers. - * Any number of headers may be added, updated and - * removed regardless of the initial count. - */ -RD_EXPORT rd_kafka_headers_t *rd_kafka_headers_new(size_t initial_count); - -/** - * @brief Destroy the headers list. The object and any returned value pointers - * are not usable after this call. - */ -RD_EXPORT void rd_kafka_headers_destroy(rd_kafka_headers_t *hdrs); - -/** - * @brief Make a copy of headers list \p src. - */ -RD_EXPORT rd_kafka_headers_t * -rd_kafka_headers_copy(const rd_kafka_headers_t *src); - -/** - * @brief Add header with name \p name and value \p val (copied) of size - * \p size (not including null-terminator). - * - * @param hdrs Headers list. - * @param name Header name. - * @param name_size Header name size (not including the null-terminator). - * If -1 the \p name length is automatically acquired using - * strlen(). - * @param value Pointer to header value, or NULL (set size to 0 or -1). - * @param value_size Size of header value. If -1 the \p value is assumed to be a - * null-terminated string and the length is automatically - * acquired using strlen(). - * - * @returns RD_KAFKA_RESP_ERR__READ_ONLY if the headers are read-only, - * else RD_KAFKA_RESP_ERR_NO_ERROR. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_header_add(rd_kafka_headers_t *hdrs, - const char *name, - ssize_t name_size, - const void *value, - ssize_t value_size); - -/** - * @brief Remove all headers for the given key (if any). - * - * @returns RD_KAFKA_RESP_ERR__READ_ONLY if the headers are read-only, - * RD_KAFKA_RESP_ERR__NOENT if no matching headers were found, - * else RD_KAFKA_RESP_ERR_NO_ERROR if headers were removed. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_header_remove(rd_kafka_headers_t *hdrs, - const char *name); - - -/** - * @brief Find last header in list \p hdrs matching \p name. - * - * @param hdrs Headers list. - * @param name Header to find (last match). - * @param valuep (out) Set to a (null-terminated) const pointer to the value - * (may be NULL). - * @param sizep (out) Set to the value's size (not including null-terminator). - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR if an entry was found, else - * RD_KAFKA_RESP_ERR__NOENT. - * - * @remark The returned pointer in \p valuep includes a trailing null-terminator - * that is not accounted for in \p sizep. - * @remark The returned pointer is only valid as long as the headers list and - * the header item is valid. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_header_get_last(const rd_kafka_headers_t *hdrs, - const char *name, - const void **valuep, - size_t *sizep); - -/** - * @brief Iterator for headers matching \p name. - * - * Same semantics as rd_kafka_header_get_last() - * - * @param hdrs Headers to iterate. - * @param idx Iterator index, start at 0 and increment by one for each call - * as long as RD_KAFKA_RESP_ERR_NO_ERROR is returned. - * @param name Header name to match. - * @param valuep (out) Set to a (null-terminated) const pointer to the value - * (may be NULL). - * @param sizep (out) Set to the value's size (not including null-terminator). - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_header_get(const rd_kafka_headers_t *hdrs, - size_t idx, - const char *name, - const void **valuep, - size_t *sizep); - - -/** - * @brief Iterator for all headers. - * - * Same semantics as rd_kafka_header_get() - * - * @sa rd_kafka_header_get() - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_header_get_all(const rd_kafka_headers_t *hdrs, - size_t idx, - const char **namep, - const void **valuep, - size_t *sizep); - - - -/**@}*/ - - - -/** - * @name Kafka messages - * @{ - * - */ - - - -// FIXME: This doesn't show up in docs for some reason -// "Compound rd_kafka_message_t is not documented." - -/** - * @brief A Kafka message as returned by the \c rd_kafka_consume*() family - * of functions as well as provided to the Producer \c dr_msg_cb(). - * - * For the consumer this object has two purposes: - * - provide the application with a consumed message. (\c err == 0) - * - report per-topic+partition consumer errors (\c err != 0) - * - * The application must check \c err to decide what action to take. - * - * When the application is finished with a message it must call - * rd_kafka_message_destroy() unless otherwise noted. - */ -typedef struct rd_kafka_message_s { - rd_kafka_resp_err_t err; /**< Non-zero for error signaling. */ - rd_kafka_topic_t *rkt; /**< Topic */ - int32_t partition; /**< Partition */ - void *payload; /**< Producer: original message payload. - * Consumer: Depends on the value of \c err : - * - \c err==0: Message payload. - * - \c err!=0: Error string */ - size_t len; /**< Depends on the value of \c err : - * - \c err==0: Message payload length - * - \c err!=0: Error string length */ - void *key; /**< Depends on the value of \c err : - * - \c err==0: Optional message key */ - size_t key_len; /**< Depends on the value of \c err : - * - \c err==0: Optional message key length*/ - int64_t offset; /**< Consumer: - * - Message offset (or offset for error - * if \c err!=0 if applicable). - * Producer, dr_msg_cb: - * Message offset assigned by broker. - * May be RD_KAFKA_OFFSET_INVALID - * for retried messages when - * idempotence is enabled. */ - void *_private; /**< Consumer: - * - rdkafka private pointer: DO NOT MODIFY - * Producer: - * - dr_msg_cb: - * msg_opaque from produce() call or - * RD_KAFKA_V_OPAQUE from producev(). */ -} rd_kafka_message_t; - - -/** - * @brief Frees resources for \p rkmessage and hands ownership back to rdkafka. - */ -RD_EXPORT -void rd_kafka_message_destroy(rd_kafka_message_t *rkmessage); - - - -/** - * @brief Returns the error string for an errored rd_kafka_message_t or NULL if - * there was no error. - * - * @remark This function MUST NOT be used with the producer. - */ -RD_EXPORT -const char *rd_kafka_message_errstr(const rd_kafka_message_t *rkmessage); - - -/** - * @brief Returns the message timestamp for a consumed message. - * - * The timestamp is the number of milliseconds since the epoch (UTC). - * - * \p tstype (if not NULL) is updated to indicate the type of timestamp. - * - * @returns message timestamp, or -1 if not available. - * - * @remark Message timestamps require broker version 0.10.0 or later. - */ -RD_EXPORT -int64_t rd_kafka_message_timestamp(const rd_kafka_message_t *rkmessage, - rd_kafka_timestamp_type_t *tstype); - - - -/** - * @brief Returns the latency for a produced message measured from - * the produce() call. - * - * @returns the latency in microseconds, or -1 if not available. - */ -RD_EXPORT -int64_t rd_kafka_message_latency(const rd_kafka_message_t *rkmessage); - - -/** - * @brief Returns the broker id of the broker the message was produced to - * or fetched from. - * - * @returns a broker id if known, else -1. - */ -RD_EXPORT -int32_t rd_kafka_message_broker_id(const rd_kafka_message_t *rkmessage); - - -/** - * @brief Get the message header list. - * - * The returned pointer in \p *hdrsp is associated with the \p rkmessage and - * must not be used after destruction of the message object or the header - * list is replaced with rd_kafka_message_set_headers(). - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR if headers were returned, - * RD_KAFKA_RESP_ERR__NOENT if the message has no headers, - * or another error code if the headers could not be parsed. - * - * @remark Headers require broker version 0.11.0.0 or later. - * - * @remark As an optimization the raw protocol headers are parsed on - * the first call to this function. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_message_headers(const rd_kafka_message_t *rkmessage, - rd_kafka_headers_t **hdrsp); - -/** - * @brief Get the message header list and detach the list from the message - * making the application the owner of the headers. - * The application must eventually destroy the headers using - * rd_kafka_headers_destroy(). - * The message's headers will be set to NULL. - * - * Otherwise same semantics as rd_kafka_message_headers() - * - * @sa rd_kafka_message_headers - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_message_detach_headers(rd_kafka_message_t *rkmessage, - rd_kafka_headers_t **hdrsp); - - -/** - * @brief Replace the message's current headers with a new list. - * - * @param rkmessage The message to set headers. - * @param hdrs New header list. The message object assumes ownership of - * the list, the list will be destroyed automatically with - * the message object. - * The new headers list may be updated until the message object - * is passed or returned to librdkafka. - * - * @remark The existing headers object, if any, will be destroyed. - */ -RD_EXPORT -void rd_kafka_message_set_headers(rd_kafka_message_t *rkmessage, - rd_kafka_headers_t *hdrs); - - -/** - * @brief Returns the number of header key/value pairs - * - * @param hdrs Headers to count - */ -RD_EXPORT size_t rd_kafka_header_cnt(const rd_kafka_headers_t *hdrs); - - -/** - * @enum rd_kafka_msg_status_t - * @brief Message persistence status can be used by the application to - * find out if a produced message was persisted in the topic log. - */ -typedef enum { - /** Message was never transmitted to the broker, or failed with - * an error indicating it was not written to the log. - * Application retry risks ordering, but not duplication. */ - RD_KAFKA_MSG_STATUS_NOT_PERSISTED = 0, - - /** Message was transmitted to broker, but no acknowledgement was - * received. - * Application retry risks ordering and duplication. */ - RD_KAFKA_MSG_STATUS_POSSIBLY_PERSISTED = 1, - - /** Message was written to the log and acknowledged by the broker. - * No reason for application to retry. - * Note: this value should only be trusted with \c acks=all. */ - RD_KAFKA_MSG_STATUS_PERSISTED = 2 -} rd_kafka_msg_status_t; - - -/** - * @brief Returns the message's persistence status in the topic log. - * - * @remark The message status is not available in on_acknowledgement - * interceptors. - */ -RD_EXPORT rd_kafka_msg_status_t -rd_kafka_message_status(const rd_kafka_message_t *rkmessage); - -/**@}*/ - - -/** - * @name Configuration interface - * @{ - * - * @brief Main/global configuration property interface - * - */ - -/** - * @enum rd_kafka_conf_res_t - * @brief Configuration result type - */ -typedef enum { - RD_KAFKA_CONF_UNKNOWN = -2, /**< Unknown configuration name. */ - RD_KAFKA_CONF_INVALID = -1, /**< Invalid configuration value or - * property or value not supported in - * this build. */ - RD_KAFKA_CONF_OK = 0 /**< Configuration okay */ -} rd_kafka_conf_res_t; - - -/** - * @brief Create configuration object. - * - * When providing your own configuration to the \c rd_kafka_*_new_*() calls - * the rd_kafka_conf_t objects needs to be created with this function - * which will set up the defaults. - * I.e.: - * @code - * rd_kafka_conf_t *myconf; - * rd_kafka_conf_res_t res; - * - * myconf = rd_kafka_conf_new(); - * res = rd_kafka_conf_set(myconf, "socket.timeout.ms", "600", - * errstr, sizeof(errstr)); - * if (res != RD_KAFKA_CONF_OK) - * die("%s\n", errstr); - * - * rk = rd_kafka_new(..., myconf); - * @endcode - * - * Please see CONFIGURATION.md for the default settings or use - * rd_kafka_conf_properties_show() to provide the information at runtime. - * - * The properties are identical to the Apache Kafka configuration properties - * whenever possible. - * - * @remark A successful call to rd_kafka_new() will assume ownership of - * the conf object and rd_kafka_conf_destroy() must not be called. - * - * @returns A new rd_kafka_conf_t object with defaults set. - * - * @sa rd_kafka_new(), rd_kafka_conf_set(), rd_kafka_conf_destroy() - */ -RD_EXPORT -rd_kafka_conf_t *rd_kafka_conf_new(void); - - -/** - * @brief Destroys a conf object. - */ -RD_EXPORT -void rd_kafka_conf_destroy(rd_kafka_conf_t *conf); - - -/** - * @brief Creates a copy/duplicate of configuration object \p conf - * - * @remark Interceptors are NOT copied to the new configuration object. - * @sa rd_kafka_interceptor_f_on_conf_dup - */ -RD_EXPORT -rd_kafka_conf_t *rd_kafka_conf_dup(const rd_kafka_conf_t *conf); - - -/** - * @brief Same as rd_kafka_conf_dup() but with an array of property name - * prefixes to filter out (ignore) when copying. - */ -RD_EXPORT -rd_kafka_conf_t *rd_kafka_conf_dup_filter(const rd_kafka_conf_t *conf, - size_t filter_cnt, - const char **filter); - - - -/** - * @returns the configuration object used by an rd_kafka_t instance. - * For use with rd_kafka_conf_get(), et.al., to extract configuration - * properties from a running client. - * - * @remark the returned object is read-only and its lifetime is the same - * as the rd_kafka_t object. - */ -RD_EXPORT -const rd_kafka_conf_t *rd_kafka_conf(rd_kafka_t *rk); - - -/** - * @brief Sets a configuration property. - * - * \p conf must have been previously created with rd_kafka_conf_new(). - * - * Fallthrough: - * Topic-level configuration properties may be set using this interface - * in which case they are applied on the \c default_topic_conf. - * If no \c default_topic_conf has been set one will be created. - * Any sub-sequent rd_kafka_conf_set_default_topic_conf() calls will - * replace the current default topic configuration. - * - * @returns \c rd_kafka_conf_res_t to indicate success or failure. - * In case of failure \p errstr is updated to contain a human readable - * error string. - * - * @remark Setting properties or values that were disabled at build time due to - * missing dependencies will return RD_KAFKA_CONF_INVALID. - */ -RD_EXPORT -rd_kafka_conf_res_t rd_kafka_conf_set(rd_kafka_conf_t *conf, - const char *name, - const char *value, - char *errstr, - size_t errstr_size); - - -/** - * @brief Enable event sourcing. - * \p events is a bitmask of \c RD_KAFKA_EVENT_* of events to enable - * for consumption by `rd_kafka_queue_poll()`. - */ -RD_EXPORT -void rd_kafka_conf_set_events(rd_kafka_conf_t *conf, int events); - - -/** - * @brief Generic event callback to be used with the event API to trigger - * callbacks for \c rd_kafka_event_t objects from a background - * thread serving the background queue. - * - * How to use: - * 1. First set the event callback on the configuration object with this - * function, followed by creating an rd_kafka_t instance - * with rd_kafka_new(). - * 2. Get the instance's background queue with rd_kafka_queue_get_background() - * and pass it as the reply/response queue to an API that takes an - * event queue, such as rd_kafka_CreateTopics(). - * 3. As the response event is ready and enqueued on the background queue the - * event callback will be triggered from the background thread. - * 4. Prior to destroying the client instance, loose your reference to the - * background queue by calling rd_kafka_queue_destroy(). - * - * The application must destroy the \c rkev passed to \p event cb using - * rd_kafka_event_destroy(). - * - * The \p event_cb \c opaque argument is the opaque set with - * rd_kafka_conf_set_opaque(). - * - * @remark This callback is a specialized alternative to the poll-based - * event API described in the Event interface section. - * - * @remark The \p event_cb will be called spontaneously from a background - * thread completely managed by librdkafka. - * Take care to perform proper locking of application objects. - * - * @warning The application MUST NOT call rd_kafka_destroy() from the - * event callback. - * - * @sa rd_kafka_queue_get_background - */ -RD_EXPORT void rd_kafka_conf_set_background_event_cb( - rd_kafka_conf_t *conf, - void (*event_cb)(rd_kafka_t *rk, rd_kafka_event_t *rkev, void *opaque)); - - -/** - * @deprecated See rd_kafka_conf_set_dr_msg_cb() - */ -RD_EXPORT -void rd_kafka_conf_set_dr_cb(rd_kafka_conf_t *conf, - void (*dr_cb)(rd_kafka_t *rk, - void *payload, - size_t len, - rd_kafka_resp_err_t err, - void *opaque, - void *msg_opaque)); - -/** - * @brief \b Producer: Set delivery report callback in provided \p conf object. - * - * The delivery report callback will be called once for each message - * accepted by rd_kafka_produce() (et.al) with \p err set to indicate - * the result of the produce request. - * - * The callback is called when a message is succesfully produced or - * if librdkafka encountered a permanent failure. - * Delivery errors occur when the retry count is exceeded, when the - * message.timeout.ms timeout is exceeded or there is a permanent error - * like RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART. - * - * An application must call rd_kafka_poll() at regular intervals to - * serve queued delivery report callbacks. - * - * The broker-assigned offset can be retrieved with \c rkmessage->offset - * and the timestamp can be retrieved using rd_kafka_message_timestamp(). - * - * The \p dr_msg_cb \c opaque argument is the opaque set with - * rd_kafka_conf_set_opaque(). - * The per-message msg_opaque value is available in - * \c rd_kafka_message_t._private. - * - * @remark The Idempotent Producer may return invalid timestamp - * (RD_KAFKA_TIMESTAMP_NOT_AVAILABLE), and - * and offset (RD_KAFKA_OFFSET_INVALID) for retried messages - * that were previously successfully delivered but not properly - * acknowledged. - */ -RD_EXPORT -void rd_kafka_conf_set_dr_msg_cb( - rd_kafka_conf_t *conf, - void (*dr_msg_cb)(rd_kafka_t *rk, - const rd_kafka_message_t *rkmessage, - void *opaque)); - - -/** - * @brief \b Consumer: Set consume callback for use with - * rd_kafka_consumer_poll() - * - * The \p consume_cb \p opaque argument is the opaque set with - * rd_kafka_conf_set_opaque(). - */ -RD_EXPORT -void rd_kafka_conf_set_consume_cb( - rd_kafka_conf_t *conf, - void (*consume_cb)(rd_kafka_message_t *rkmessage, void *opaque)); - -/** - * @brief \b Consumer: Set rebalance callback for use with - * coordinated consumer group balancing. - * - * The \p err field is set to either RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS - * or RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS and 'partitions' - * contains the full partition set that was either assigned or revoked. - * - * Registering a \p rebalance_cb turns off librdkafka's automatic - * partition assignment/revocation and instead delegates that responsibility - * to the application's \p rebalance_cb. - * - * The rebalance callback is responsible for updating librdkafka's - * assignment set based on the two events: RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS - * and RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS but should also be able to handle - * arbitrary rebalancing failures where \p err is neither of those. - * @remark In this latter case (arbitrary error), the application must - * call rd_kafka_assign(rk, NULL) to synchronize state. - * - * For eager/non-cooperative `partition.assignment.strategy` assignors, - * such as `range` and `roundrobin`, the application must use - * rd_kafka_assign() to set or clear the entire assignment. - * For the cooperative assignors, such as `cooperative-sticky`, the application - * must use rd_kafka_incremental_assign() for - * RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS and rd_kafka_incremental_unassign() - * for RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS. - * - * Without a rebalance callback this is done automatically by librdkafka - * but registering a rebalance callback gives the application flexibility - * in performing other operations along with the assigning/revocation, - * such as fetching offsets from an alternate location (on assign) - * or manually committing offsets (on revoke). - * - * rebalance_cb is always triggered exactly once when a rebalance completes - * with a new assignment, even if that assignment is empty. If an - * eager/non-cooperative assignor is configured, there will eventually be - * exactly one corresponding call to rebalance_cb to revoke these partitions - * (even if empty), whether this is due to a group rebalance or lost - * partitions. In the cooperative case, rebalance_cb will never be called if - * the set of partitions being revoked is empty (whether or not lost). - * - * The callback's \p opaque argument is the opaque set with - * rd_kafka_conf_set_opaque(). - * - * @remark The \p partitions list is destroyed by librdkafka on return - * return from the rebalance_cb and must not be freed or - * saved by the application. - * - * @remark Be careful when modifying the \p partitions list. - * Changing this list should only be done to change the initial - * offsets for each partition. - * But a function like `rd_kafka_position()` might have unexpected - * effects for instance when a consumer gets assigned a partition - * it used to consume at an earlier rebalance. In this case, the - * list of partitions will be updated with the old offset for that - * partition. In this case, it is generally better to pass a copy - * of the list (see `rd_kafka_topic_partition_list_copy()`). - * The result of `rd_kafka_position()` is typically outdated in - * RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS. - * - * @sa rd_kafka_assign() - * @sa rd_kafka_incremental_assign() - * @sa rd_kafka_incremental_unassign() - * @sa rd_kafka_assignment_lost() - * @sa rd_kafka_rebalance_protocol() - * - * The following example shows the application's responsibilities: - * @code - * static void rebalance_cb (rd_kafka_t *rk, rd_kafka_resp_err_t err, - * rd_kafka_topic_partition_list_t *partitions, - * void *opaque) { - * - * switch (err) - * { - * case RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS: - * // application may load offets from arbitrary external - * // storage here and update \p partitions - * if (!strcmp(rd_kafka_rebalance_protocol(rk), "COOPERATIVE")) - * rd_kafka_incremental_assign(rk, partitions); - * else // EAGER - * rd_kafka_assign(rk, partitions); - * break; - * - * case RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS: - * if (manual_commits) // Optional explicit manual commit - * rd_kafka_commit(rk, partitions, 0); // sync commit - * - * if (!strcmp(rd_kafka_rebalance_protocol(rk), "COOPERATIVE")) - * rd_kafka_incremental_unassign(rk, partitions); - * else // EAGER - * rd_kafka_assign(rk, NULL); - * break; - * - * default: - * handle_unlikely_error(err); - * rd_kafka_assign(rk, NULL); // sync state - * break; - * } - * } - * @endcode - * - * @remark The above example lacks error handling for assign calls, see - * the examples/ directory. - */ -RD_EXPORT -void rd_kafka_conf_set_rebalance_cb( - rd_kafka_conf_t *conf, - void (*rebalance_cb)(rd_kafka_t *rk, - rd_kafka_resp_err_t err, - rd_kafka_topic_partition_list_t *partitions, - void *opaque)); - - - -/** - * @brief \b Consumer: Set offset commit callback for use with consumer groups. - * - * The results of automatic or manual offset commits will be scheduled - * for this callback and is served by rd_kafka_consumer_poll(). - * - * If no partitions had valid offsets to commit this callback will be called - * with \p err == RD_KAFKA_RESP_ERR__NO_OFFSET which is not to be considered - * an error. - * - * The \p offsets list contains per-partition information: - * - \c offset: committed offset (attempted) - * - \c err: commit error - * - * The callback's \p opaque argument is the opaque set with - * rd_kafka_conf_set_opaque(). - */ -RD_EXPORT -void rd_kafka_conf_set_offset_commit_cb( - rd_kafka_conf_t *conf, - void (*offset_commit_cb)(rd_kafka_t *rk, - rd_kafka_resp_err_t err, - rd_kafka_topic_partition_list_t *offsets, - void *opaque)); - - -/** - * @brief Set error callback in provided conf object. - * - * The error callback is used by librdkafka to signal warnings and errors - * back to the application. - * - * These errors should generally be considered informational and non-permanent, - * the client will try to recover automatically from all type of errors. - * Given that the client and cluster configuration is correct the - * application should treat these as temporary errors. - * - * \p error_cb will be triggered with \c err set to RD_KAFKA_RESP_ERR__FATAL - * if a fatal error has been raised; in this case use rd_kafka_fatal_error() to - * retrieve the fatal error code and error string, and then begin terminating - * the client instance. - * - * If no \p error_cb is registered, or RD_KAFKA_EVENT_ERROR has not been set - * with rd_kafka_conf_set_events, then the errors will be logged instead. - * - * The callback's \p opaque argument is the opaque set with - * rd_kafka_conf_set_opaque(). - */ -RD_EXPORT -void rd_kafka_conf_set_error_cb(rd_kafka_conf_t *conf, - void (*error_cb)(rd_kafka_t *rk, - int err, - const char *reason, - void *opaque)); - -/** - * @brief Set throttle callback. - * - * The throttle callback is used to forward broker throttle times to the - * application for Produce and Fetch (consume) requests. - * - * Callbacks are triggered whenever a non-zero throttle time is returned by - * the broker, or when the throttle time drops back to zero. - * - * An application must call rd_kafka_poll() or rd_kafka_consumer_poll() at - * regular intervals to serve queued callbacks. - * - * The callback's \p opaque argument is the opaque set with - * rd_kafka_conf_set_opaque(). - * - * @remark Requires broker version 0.9.0 or later. - */ -RD_EXPORT -void rd_kafka_conf_set_throttle_cb(rd_kafka_conf_t *conf, - void (*throttle_cb)(rd_kafka_t *rk, - const char *broker_name, - int32_t broker_id, - int throttle_time_ms, - void *opaque)); - - -/** - * @brief Set logger callback. - * - * The default is to print to stderr, but a syslog logger is also available, - * see rd_kafka_log_print and rd_kafka_log_syslog for the builtin alternatives. - * Alternatively the application may provide its own logger callback. - * Or pass \p func as NULL to disable logging. - * - * This is the configuration alternative to the deprecated rd_kafka_set_logger() - * - * @remark The log_cb will be called spontaneously from librdkafka's internal - * threads unless logs have been forwarded to a poll queue through - * \c rd_kafka_set_log_queue(). - * An application MUST NOT call any librdkafka APIs or do any prolonged - * work in a non-forwarded \c log_cb. - */ -RD_EXPORT -void rd_kafka_conf_set_log_cb(rd_kafka_conf_t *conf, - void (*log_cb)(const rd_kafka_t *rk, - int level, - const char *fac, - const char *buf)); - - -/** - * @brief Set statistics callback in provided conf object. - * - * The statistics callback is triggered from rd_kafka_poll() every - * \c statistics.interval.ms (needs to be configured separately). - * Function arguments: - * - \p rk - Kafka handle - * - \p json - String containing the statistics data in JSON format - * - \p json_len - Length of \p json string. - * - \p opaque - Application-provided opaque as set by - * rd_kafka_conf_set_opaque(). - * - * For more information on the format of \p json, see - * https://github.com/edenhill/librdkafka/wiki/Statistics - * - * If the application wishes to hold on to the \p json pointer and free - * it at a later time it must return 1 from the \p stats_cb. - * If the application returns 0 from the \p stats_cb then librdkafka - * will immediately free the \p json pointer. - * - * See STATISTICS.md for a full definition of the JSON object. - */ -RD_EXPORT -void rd_kafka_conf_set_stats_cb( - rd_kafka_conf_t *conf, - int (*stats_cb)(rd_kafka_t *rk, char *json, size_t json_len, void *opaque)); - -/** - * @brief Set SASL/OAUTHBEARER token refresh callback in provided conf object. - * - * @param conf the configuration to mutate. - * @param oauthbearer_token_refresh_cb the callback to set; callback function - * arguments:
- * \p rk - Kafka handle
- * \p oauthbearer_config - Value of configuration property - * sasl.oauthbearer.config. - * \p opaque - Application-provided opaque set via - * rd_kafka_conf_set_opaque() - * - * The SASL/OAUTHBEARER token refresh callback is triggered via rd_kafka_poll() - * whenever OAUTHBEARER is the SASL mechanism and a token needs to be retrieved, - * typically based on the configuration defined in \c sasl.oauthbearer.config. - * - * The callback should invoke rd_kafka_oauthbearer_set_token() - * or rd_kafka_oauthbearer_set_token_failure() to indicate success - * or failure, respectively. - * - * The refresh operation is eventable and may be received via - * rd_kafka_queue_poll() with an event type of - * \c RD_KAFKA_EVENT_OAUTHBEARER_TOKEN_REFRESH. - * - * Note that before any SASL/OAUTHBEARER broker connection can succeed the - * application must call rd_kafka_oauthbearer_set_token() once -- either - * directly or, more typically, by invoking either rd_kafka_poll(), - * rd_kafka_consumer_poll(), rd_kafka_queue_poll(), etc, in order to cause - * retrieval of an initial token to occur. - * - * Alternatively, the application can enable the SASL queue by calling - * rd_kafka_conf_enable_sasl_queue() on the configuration object prior to - * creating the client instance, get the SASL queue with - * rd_kafka_queue_get_sasl(), and either serve the queue manually by calling - * rd_kafka_queue_poll(), or redirecting the queue to the background thread to - * have the queue served automatically. For the latter case the SASL queue - * must be forwarded to the background queue with rd_kafka_queue_forward(). - * A convenience function is available to automatically forward the SASL queue - * to librdkafka's background thread, see - * rd_kafka_sasl_background_callbacks_enable(). - * - * An unsecured JWT refresh handler is provided by librdkafka for development - * and testing purposes, it is enabled by setting - * the \c enable.sasl.oauthbearer.unsecure.jwt property to true and is - * mutually exclusive to using a refresh callback. - * - * @sa rd_kafka_sasl_background_callbacks_enable() - * @sa rd_kafka_queue_get_sasl() - */ -RD_EXPORT -void rd_kafka_conf_set_oauthbearer_token_refresh_cb( - rd_kafka_conf_t *conf, - void (*oauthbearer_token_refresh_cb)(rd_kafka_t *rk, - const char *oauthbearer_config, - void *opaque)); - -/** - * @brief Enable/disable creation of a queue specific to SASL events - * and callbacks. - * - * For SASL mechanisms that trigger callbacks (currently OAUTHBEARER) this - * configuration API allows an application to get a dedicated - * queue for the SASL events/callbacks. After enabling the queue with this API - * the application can retrieve the queue by calling - * rd_kafka_queue_get_sasl() on the client instance. - * This queue may then be served directly by the application - * (with rd_kafka_queue_poll(), et.al) or forwarded to another queue, such as - * the background queue. - * - * A convenience function is available to automatically forward the SASL queue - * to librdkafka's background thread, see - * rd_kafka_sasl_background_callbacks_enable(). - * - * By default (\p enable = 0) the main queue (as served by rd_kafka_poll(), - * et.al.) is used for SASL callbacks. - * - * @remark The SASL queue is currently only used by the SASL OAUTHBEARER - * mechanism's token_refresh_cb(). - * - * @sa rd_kafka_queue_get_sasl() - * @sa rd_kafka_sasl_background_callbacks_enable() - */ - -RD_EXPORT -void rd_kafka_conf_enable_sasl_queue(rd_kafka_conf_t *conf, int enable); - - -/** - * @brief Set socket callback. - * - * The socket callback is responsible for opening a socket - * according to the supplied \p domain, \p type and \p protocol. - * The socket shall be created with \c CLOEXEC set in a racefree fashion, if - * possible. - * - * The callback's \p opaque argument is the opaque set with - * rd_kafka_conf_set_opaque(). - * - * Default: - * - on linux: racefree CLOEXEC - * - others : non-racefree CLOEXEC - * - * @remark The callback will be called from an internal librdkafka thread. - */ -RD_EXPORT -void rd_kafka_conf_set_socket_cb( - rd_kafka_conf_t *conf, - int (*socket_cb)(int domain, int type, int protocol, void *opaque)); - - - -/** - * @brief Set connect callback. - * - * The connect callback is responsible for connecting socket \p sockfd - * to peer address \p addr. - * The \p id field contains the broker identifier. - * - * \p connect_cb shall return 0 on success (socket connected) or an error - * number (errno) on error. - * - * The callback's \p opaque argument is the opaque set with - * rd_kafka_conf_set_opaque(). - * - * @remark The callback will be called from an internal librdkafka thread. - */ -RD_EXPORT void -rd_kafka_conf_set_connect_cb(rd_kafka_conf_t *conf, - int (*connect_cb)(int sockfd, - const struct sockaddr *addr, - int addrlen, - const char *id, - void *opaque)); - -/** - * @brief Set close socket callback. - * - * Close a socket (optionally opened with socket_cb()). - * - * The callback's \p opaque argument is the opaque set with - * rd_kafka_conf_set_opaque(). - * - * @remark The callback will be called from an internal librdkafka thread. - */ -RD_EXPORT void rd_kafka_conf_set_closesocket_cb( - rd_kafka_conf_t *conf, - int (*closesocket_cb)(int sockfd, void *opaque)); - - - -#ifndef _WIN32 -/** - * @brief Set open callback. - * - * The open callback is responsible for opening the file specified by - * pathname, flags and mode. - * The file shall be opened with \c CLOEXEC set in a racefree fashion, if - * possible. - * - * Default: - * - on linux: racefree CLOEXEC - * - others : non-racefree CLOEXEC - * - * The callback's \p opaque argument is the opaque set with - * rd_kafka_conf_set_opaque(). - * - * @remark The callback will be called from an internal librdkafka thread. - */ -RD_EXPORT -void rd_kafka_conf_set_open_cb( - rd_kafka_conf_t *conf, - int (*open_cb)(const char *pathname, int flags, mode_t mode, void *opaque)); -#endif - - -/** - * @brief Sets the verification callback of the broker certificate - * - * The verification callback is triggered from internal librdkafka threads - * upon connecting to a broker. On each connection attempt the callback - * will be called for each certificate in the broker's certificate chain, - * starting at the root certification, as long as the application callback - * returns 1 (valid certificate). - * \c broker_name and \c broker_id correspond to the broker the connection - * is being made to. - * The \c x509_error argument indicates if OpenSSL's verification of - * the certificate succeed (0) or failed (an OpenSSL error code). - * The application may set the SSL context error code by returning 0 - * from the verify callback and providing a non-zero SSL context error code - * in \c x509_error. - * If the verify callback sets \c x509_error to 0, returns 1, and the - * original \c x509_error was non-zero, the error on the SSL context will - * be cleared. - * \c x509_error is always a valid pointer to an int. - * - * \c depth is the depth of the current certificate in the chain, starting - * at the root certificate. - * - * The certificate itself is passed in binary DER format in \c buf of - * size \c size. - * - * The callback must return 1 if verification succeeds, or - * 0 if verification fails and then write a human-readable error message - * to \c errstr (limited to \c errstr_size bytes, including nul-term). - * - * The callback's \p opaque argument is the opaque set with - * rd_kafka_conf_set_opaque(). - * - * @returns RD_KAFKA_CONF_OK if SSL is supported in this build, else - * RD_KAFKA_CONF_INVALID. - * - * @warning This callback will be called from internal librdkafka threads. - * - * @remark See in the OpenSSL source distribution - * for a list of \p x509_error codes. - */ -RD_EXPORT -rd_kafka_conf_res_t rd_kafka_conf_set_ssl_cert_verify_cb( - rd_kafka_conf_t *conf, - int (*ssl_cert_verify_cb)(rd_kafka_t *rk, - const char *broker_name, - int32_t broker_id, - int *x509_error, - int depth, - const char *buf, - size_t size, - char *errstr, - size_t errstr_size, - void *opaque)); - - -/** - * @enum rd_kafka_cert_type_t - * - * @brief SSL certificate type - * - * @sa rd_kafka_conf_set_ssl_cert - */ -typedef enum rd_kafka_cert_type_t { - RD_KAFKA_CERT_PUBLIC_KEY, /**< Client's public key */ - RD_KAFKA_CERT_PRIVATE_KEY, /**< Client's private key */ - RD_KAFKA_CERT_CA, /**< CA certificate */ - RD_KAFKA_CERT__CNT, -} rd_kafka_cert_type_t; - -/** - * @enum rd_kafka_cert_enc_t - * - * @brief SSL certificate encoding - * - * @sa rd_kafka_conf_set_ssl_cert - */ -typedef enum rd_kafka_cert_enc_t { - RD_KAFKA_CERT_ENC_PKCS12, /**< PKCS#12 */ - RD_KAFKA_CERT_ENC_DER, /**< DER / binary X.509 ASN1 */ - RD_KAFKA_CERT_ENC_PEM, /**< PEM */ - RD_KAFKA_CERT_ENC__CNT, -} rd_kafka_cert_enc_t; - - -/** - * @brief Set certificate/key \p cert_type from the \p cert_enc encoded - * memory at \p buffer of \p size bytes. - * - * @param conf Configuration object. - * @param cert_type Certificate or key type to configure. - * @param cert_enc Buffer \p encoding type. - * @param buffer Memory pointer to encoded certificate or key. - * The memory is not referenced after this function returns. - * @param size Size of memory at \p buffer. - * @param errstr Memory were a human-readable error string will be written - * on failure. - * @param errstr_size Size of \p errstr, including space for nul-terminator. - * - * @returns RD_KAFKA_CONF_OK on success or RD_KAFKA_CONF_INVALID if the - * memory in \p buffer is of incorrect encoding, or if librdkafka - * was not built with SSL support. - * - * @remark Calling this method multiple times with the same \p cert_type - * will replace the previous value. - * - * @remark Calling this method with \p buffer set to NULL will clear the - * configuration for \p cert_type. - * - * @remark The private key may require a password, which must be specified - * with the `ssl.key.password` configuration property prior to - * calling this function. - * - * @remark Private and public keys in PEM format may also be set with the - * `ssl.key.pem` and `ssl.certificate.pem` configuration properties. - * - * @remark CA certificate in PEM format may also be set with the - * `ssl.ca.pem` configuration property. - */ -RD_EXPORT rd_kafka_conf_res_t -rd_kafka_conf_set_ssl_cert(rd_kafka_conf_t *conf, - rd_kafka_cert_type_t cert_type, - rd_kafka_cert_enc_t cert_enc, - const void *buffer, - size_t size, - char *errstr, - size_t errstr_size); - - -/** - * @brief Set callback_data for OpenSSL engine. - * - * @param conf Configuration object. - * @param callback_data passed to engine callbacks, - * e.g. \c ENGINE_load_ssl_client_cert. - * - * @remark The \c ssl.engine.location configuration must be set for this - * to have affect. - * - * @remark The memory pointed to by \p value must remain valid for the - * lifetime of the configuration object and any Kafka clients that - * use it. - */ -RD_EXPORT -void rd_kafka_conf_set_engine_callback_data(rd_kafka_conf_t *conf, - void *callback_data); - - -/** - * @brief Sets the application's opaque pointer that will be passed to callbacks - * - * @sa rd_kafka_opaque() - */ -RD_EXPORT -void rd_kafka_conf_set_opaque(rd_kafka_conf_t *conf, void *opaque); - -/** - * @brief Retrieves the opaque pointer previously set - * with rd_kafka_conf_set_opaque() - */ -RD_EXPORT -void *rd_kafka_opaque(const rd_kafka_t *rk); - - - -/** - * @brief Sets the default topic configuration to use for automatically - * subscribed topics (e.g., through pattern-matched topics). - * The topic config object is not usable after this call. - * - * @warning Any topic configuration settings that have been set on the - * global rd_kafka_conf_t object will be overwritten by this call - * since the implicitly created default topic config object is - * replaced by the user-supplied one. - * - * @deprecated Set default topic level configuration on the - * global rd_kafka_conf_t object instead. - */ -RD_EXPORT -void rd_kafka_conf_set_default_topic_conf(rd_kafka_conf_t *conf, - rd_kafka_topic_conf_t *tconf); - -/** - * @brief Gets the default topic configuration as previously set with - * rd_kafka_conf_set_default_topic_conf() or that was implicitly created - * by configuring a topic-level property on the global \p conf object. - * - * @returns the \p conf's default topic configuration (if any), or NULL. - * - * @warning The returned topic configuration object is owned by the \p conf - * object. It may be modified but not destroyed and its lifetime is - * the same as the \p conf object or the next call to - * rd_kafka_conf_set_default_topic_conf(). - */ -RD_EXPORT rd_kafka_topic_conf_t * -rd_kafka_conf_get_default_topic_conf(rd_kafka_conf_t *conf); - - -/** - * @brief Retrieve configuration value for property \p name. - * - * If \p dest is non-NULL the value will be written to \p dest with at - * most \p dest_size. - * - * \p *dest_size is updated to the full length of the value, thus if - * \p *dest_size initially is smaller than the full length the application - * may reallocate \p dest to fit the returned \p *dest_size and try again. - * - * If \p dest is NULL only the full length of the value is returned. - * - * Fallthrough: - * Topic-level configuration properties from the \c default_topic_conf - * may be retrieved using this interface. - * - * @returns \p RD_KAFKA_CONF_OK if the property name matched, else - * \p RD_KAFKA_CONF_UNKNOWN. - */ -RD_EXPORT -rd_kafka_conf_res_t rd_kafka_conf_get(const rd_kafka_conf_t *conf, - const char *name, - char *dest, - size_t *dest_size); - - -/** - * @brief Retrieve topic configuration value for property \p name. - * - * @sa rd_kafka_conf_get() - */ -RD_EXPORT -rd_kafka_conf_res_t rd_kafka_topic_conf_get(const rd_kafka_topic_conf_t *conf, - const char *name, - char *dest, - size_t *dest_size); - - -/** - * @brief Dump the configuration properties and values of \p conf to an array - * with \"key\", \"value\" pairs. - * - * The number of entries in the array is returned in \p *cntp. - * - * The dump must be freed with `rd_kafka_conf_dump_free()`. - */ -RD_EXPORT -const char **rd_kafka_conf_dump(rd_kafka_conf_t *conf, size_t *cntp); - - -/** - * @brief Dump the topic configuration properties and values of \p conf - * to an array with \"key\", \"value\" pairs. - * - * The number of entries in the array is returned in \p *cntp. - * - * The dump must be freed with `rd_kafka_conf_dump_free()`. - */ -RD_EXPORT -const char **rd_kafka_topic_conf_dump(rd_kafka_topic_conf_t *conf, - size_t *cntp); - -/** - * @brief Frees a configuration dump returned from `rd_kafka_conf_dump()` or - * `rd_kafka_topic_conf_dump(). - */ -RD_EXPORT -void rd_kafka_conf_dump_free(const char **arr, size_t cnt); - -/** - * @brief Prints a table to \p fp of all supported configuration properties, - * their default values as well as a description. - * - * @remark All properties and properties and values are shown, even those - * that have been disabled at build time due to missing dependencies. - */ -RD_EXPORT -void rd_kafka_conf_properties_show(FILE *fp); - -/**@}*/ - - -/** - * @name Topic configuration - * @{ - * - * @brief Topic configuration property interface - * - */ - - -/** - * @brief Create topic configuration object - * - * @sa Same semantics as for rd_kafka_conf_new(). - */ -RD_EXPORT -rd_kafka_topic_conf_t *rd_kafka_topic_conf_new(void); - - -/** - * @brief Creates a copy/duplicate of topic configuration object \p conf. - */ -RD_EXPORT -rd_kafka_topic_conf_t * -rd_kafka_topic_conf_dup(const rd_kafka_topic_conf_t *conf); - -/** - * @brief Creates a copy/duplicate of \p rk 's default topic configuration - * object. - */ -RD_EXPORT -rd_kafka_topic_conf_t *rd_kafka_default_topic_conf_dup(rd_kafka_t *rk); - - -/** - * @brief Destroys a topic conf object. - */ -RD_EXPORT -void rd_kafka_topic_conf_destroy(rd_kafka_topic_conf_t *topic_conf); - - -/** - * @brief Sets a single rd_kafka_topic_conf_t value by property name. - * - * \p topic_conf should have been previously set up - * with `rd_kafka_topic_conf_new()`. - * - * @returns rd_kafka_conf_res_t to indicate success or failure. - */ -RD_EXPORT -rd_kafka_conf_res_t rd_kafka_topic_conf_set(rd_kafka_topic_conf_t *conf, - const char *name, - const char *value, - char *errstr, - size_t errstr_size); - -/** - * @brief Sets the application's opaque pointer that will be passed to all topic - * callbacks as the \c rkt_opaque argument. - * - * @sa rd_kafka_topic_opaque() - */ -RD_EXPORT -void rd_kafka_topic_conf_set_opaque(rd_kafka_topic_conf_t *conf, - void *rkt_opaque); - - -/** - * @brief \b Producer: Set partitioner callback in provided topic conf object. - * - * The partitioner may be called in any thread at any time, - * it may be called multiple times for the same message/key. - * - * The callback's \p rkt_opaque argument is the opaque set by - * rd_kafka_topic_conf_set_opaque(). - * The callback's \p msg_opaque argument is the per-message opaque - * passed to produce(). - * - * Partitioner function constraints: - * - MUST NOT call any rd_kafka_*() functions except: - * rd_kafka_topic_partition_available() - * - MUST NOT block or execute for prolonged periods of time. - * - MUST return a value between 0 and partition_cnt-1, or the - * special \c RD_KAFKA_PARTITION_UA value if partitioning - * could not be performed. - */ -RD_EXPORT -void rd_kafka_topic_conf_set_partitioner_cb( - rd_kafka_topic_conf_t *topic_conf, - int32_t (*partitioner)(const rd_kafka_topic_t *rkt, - const void *keydata, - size_t keylen, - int32_t partition_cnt, - void *rkt_opaque, - void *msg_opaque)); - - -/** - * @brief \b Producer: Set message queueing order comparator callback. - * - * The callback may be called in any thread at any time, - * it may be called multiple times for the same message. - * - * Ordering comparator function constraints: - * - MUST be stable sort (same input gives same output). - * - MUST NOT call any rd_kafka_*() functions. - * - MUST NOT block or execute for prolonged periods of time. - * - * The comparator shall compare the two messages and return: - * - < 0 if message \p a should be inserted before message \p b. - * - >=0 if message \p a should be inserted after message \p b. - * - * @remark Insert sorting will be used to enqueue the message in the - * correct queue position, this comes at a cost of O(n). - * - * @remark If `queuing.strategy=fifo` new messages are enqueued to the - * tail of the queue regardless of msg_order_cmp, but retried messages - * are still affected by msg_order_cmp. - * - * @warning THIS IS AN EXPERIMENTAL API, SUBJECT TO CHANGE OR REMOVAL, - * DO NOT USE IN PRODUCTION. - */ -RD_EXPORT void rd_kafka_topic_conf_set_msg_order_cmp( - rd_kafka_topic_conf_t *topic_conf, - int (*msg_order_cmp)(const rd_kafka_message_t *a, - const rd_kafka_message_t *b)); - - -/** - * @brief Check if partition is available (has a leader broker). - * - * @returns 1 if the partition is available, else 0. - * - * @warning This function must only be called from inside a partitioner function - */ -RD_EXPORT -int rd_kafka_topic_partition_available(const rd_kafka_topic_t *rkt, - int32_t partition); - - -/******************************************************************* - * * - * Partitioners provided by rdkafka * - * * - *******************************************************************/ - -/** - * @brief Random partitioner. - * - * Will try not to return unavailable partitions. - * - * The \p rkt_opaque argument is the opaque set by - * rd_kafka_topic_conf_set_opaque(). - * The \p msg_opaque argument is the per-message opaque - * passed to produce(). - * - * @returns a random partition between 0 and \p partition_cnt - 1. - * - */ -RD_EXPORT -int32_t rd_kafka_msg_partitioner_random(const rd_kafka_topic_t *rkt, - const void *key, - size_t keylen, - int32_t partition_cnt, - void *rkt_opaque, - void *msg_opaque); - -/** - * @brief Consistent partitioner. - * - * Uses consistent hashing to map identical keys onto identical partitions. - * - * The \p rkt_opaque argument is the opaque set by - * rd_kafka_topic_conf_set_opaque(). - * The \p msg_opaque argument is the per-message opaque - * passed to produce(). - * - * @returns a \"random\" partition between 0 and \p partition_cnt - 1 based on - * the CRC value of the key - */ -RD_EXPORT -int32_t rd_kafka_msg_partitioner_consistent(const rd_kafka_topic_t *rkt, - const void *key, - size_t keylen, - int32_t partition_cnt, - void *rkt_opaque, - void *msg_opaque); - -/** - * @brief Consistent-Random partitioner. - * - * This is the default partitioner. - * Uses consistent hashing to map identical keys onto identical partitions, and - * messages without keys will be assigned via the random partitioner. - * - * The \p rkt_opaque argument is the opaque set by - * rd_kafka_topic_conf_set_opaque(). - * The \p msg_opaque argument is the per-message opaque - * passed to produce(). - * - * @returns a \"random\" partition between 0 and \p partition_cnt - 1 based on - * the CRC value of the key (if provided) - */ -RD_EXPORT -int32_t rd_kafka_msg_partitioner_consistent_random(const rd_kafka_topic_t *rkt, - const void *key, - size_t keylen, - int32_t partition_cnt, - void *rkt_opaque, - void *msg_opaque); - - -/** - * @brief Murmur2 partitioner (Java compatible). - * - * Uses consistent hashing to map identical keys onto identical partitions - * using Java-compatible Murmur2 hashing. - * - * The \p rkt_opaque argument is the opaque set by - * rd_kafka_topic_conf_set_opaque(). - * The \p msg_opaque argument is the per-message opaque - * passed to produce(). - * - * @returns a partition between 0 and \p partition_cnt - 1. - */ -RD_EXPORT -int32_t rd_kafka_msg_partitioner_murmur2(const rd_kafka_topic_t *rkt, - const void *key, - size_t keylen, - int32_t partition_cnt, - void *rkt_opaque, - void *msg_opaque); - -/** - * @brief Consistent-Random Murmur2 partitioner (Java compatible). - * - * Uses consistent hashing to map identical keys onto identical partitions - * using Java-compatible Murmur2 hashing. - * Messages without keys will be assigned via the random partitioner. - * - * The \p rkt_opaque argument is the opaque set by - * rd_kafka_topic_conf_set_opaque(). - * The \p msg_opaque argument is the per-message opaque - * passed to produce(). - * - * @returns a partition between 0 and \p partition_cnt - 1. - */ -RD_EXPORT -int32_t rd_kafka_msg_partitioner_murmur2_random(const rd_kafka_topic_t *rkt, - const void *key, - size_t keylen, - int32_t partition_cnt, - void *rkt_opaque, - void *msg_opaque); - - -/** - * @brief FNV-1a partitioner. - * - * Uses consistent hashing to map identical keys onto identical partitions - * using FNV-1a hashing. - * - * The \p rkt_opaque argument is the opaque set by - * rd_kafka_topic_conf_set_opaque(). - * The \p msg_opaque argument is the per-message opaque - * passed to produce(). - * - * @returns a partition between 0 and \p partition_cnt - 1. - */ -RD_EXPORT -int32_t rd_kafka_msg_partitioner_fnv1a(const rd_kafka_topic_t *rkt, - const void *key, - size_t keylen, - int32_t partition_cnt, - void *rkt_opaque, - void *msg_opaque); - - -/** - * @brief Consistent-Random FNV-1a partitioner. - * - * Uses consistent hashing to map identical keys onto identical partitions - * using FNV-1a hashing. - * Messages without keys will be assigned via the random partitioner. - * - * The \p rkt_opaque argument is the opaque set by - * rd_kafka_topic_conf_set_opaque(). - * The \p msg_opaque argument is the per-message opaque - * passed to produce(). - * - * @returns a partition between 0 and \p partition_cnt - 1. - */ -RD_EXPORT -int32_t rd_kafka_msg_partitioner_fnv1a_random(const rd_kafka_topic_t *rkt, - const void *key, - size_t keylen, - int32_t partition_cnt, - void *rkt_opaque, - void *msg_opaque); - - -/**@}*/ - - - -/** - * @name Main Kafka and Topic object handles - * @{ - * - * - */ - - - -/** - * @brief Creates a new Kafka handle and starts its operation according to the - * specified \p type (\p RD_KAFKA_CONSUMER or \p RD_KAFKA_PRODUCER). - * - * \p conf is an optional struct created with `rd_kafka_conf_new()` that will - * be used instead of the default configuration. - * The \p conf object is freed by this function on success and must not be used - * or destroyed by the application sub-sequently. - * See `rd_kafka_conf_set()` et.al for more information. - * - * \p errstr must be a pointer to memory of at least size \p errstr_size where - * `rd_kafka_new()` may write a human readable error message in case the - * creation of a new handle fails. In which case the function returns NULL. - * - * @remark \b RD_KAFKA_CONSUMER: When a new \p RD_KAFKA_CONSUMER - * rd_kafka_t handle is created it may either operate in the - * legacy simple consumer mode using the rd_kafka_consume_start() - * interface, or the High-level KafkaConsumer API. - * @remark An application must only use one of these groups of APIs on a given - * rd_kafka_t RD_KAFKA_CONSUMER handle. - - * - * @returns The Kafka handle on success or NULL on error (see \p errstr) - * - * @sa To destroy the Kafka handle, use rd_kafka_destroy(). - */ -RD_EXPORT -rd_kafka_t *rd_kafka_new(rd_kafka_type_t type, - rd_kafka_conf_t *conf, - char *errstr, - size_t errstr_size); - - -/** - * @brief Destroy Kafka handle. - * - * @remark This is a blocking operation. - * @remark rd_kafka_consumer_close() will be called from this function - * if the instance type is RD_KAFKA_CONSUMER, a \c group.id was - * configured, and the rd_kafka_consumer_close() was not - * explicitly called by the application. This in turn may - * trigger consumer callbacks, such as rebalance_cb. - * Use rd_kafka_destroy_flags() with - * RD_KAFKA_DESTROY_F_NO_CONSUMER_CLOSE to avoid this behaviour. - * - * @sa rd_kafka_destroy_flags() - */ -RD_EXPORT -void rd_kafka_destroy(rd_kafka_t *rk); - - -/** - * @brief Destroy Kafka handle according to specified destroy flags - * - */ -RD_EXPORT -void rd_kafka_destroy_flags(rd_kafka_t *rk, int flags); - -/** - * @brief Flags for rd_kafka_destroy_flags() - */ - -/*! - * Don't call consumer_close() to leave group and commit final offsets. - * - * This also disables consumer callbacks to be called from rd_kafka_destroy*(), - * such as rebalance_cb. - * - * The consumer group handler is still closed internally, but from an - * application perspective none of the functionality from consumer_close() - * is performed. - */ -#define RD_KAFKA_DESTROY_F_NO_CONSUMER_CLOSE 0x8 - - - -/** - * @brief Returns Kafka handle name. - */ -RD_EXPORT -const char *rd_kafka_name(const rd_kafka_t *rk); - - -/** - * @brief Returns Kafka handle type. - */ -RD_EXPORT -rd_kafka_type_t rd_kafka_type(const rd_kafka_t *rk); - - -/** - * @brief Returns this client's broker-assigned group member id. - * - * @remark This currently requires the high-level KafkaConsumer - * - * @returns An allocated string containing the current broker-assigned group - * member id, or NULL if not available. - * The application must free the string with \p free() or - * rd_kafka_mem_free() - */ -RD_EXPORT -char *rd_kafka_memberid(const rd_kafka_t *rk); - - - -/** - * @brief Returns the ClusterId as reported in broker metadata. - * - * @param rk Client instance. - * @param timeout_ms If there is no cached value from metadata retrieval - * then this specifies the maximum amount of time - * (in milliseconds) the call will block waiting - * for metadata to be retrieved. - * Use 0 for non-blocking calls. - - * @remark Requires broker version >=0.10.0 and api.version.request=true. - * - * @remark The application must free the returned pointer - * using rd_kafka_mem_free(). - * - * @returns a newly allocated string containing the ClusterId, or NULL - * if no ClusterId could be retrieved in the allotted timespan. - */ -RD_EXPORT -char *rd_kafka_clusterid(rd_kafka_t *rk, int timeout_ms); - - -/** - * @brief Returns the current ControllerId as reported in broker metadata. - * - * @param rk Client instance. - * @param timeout_ms If there is no cached value from metadata retrieval - * then this specifies the maximum amount of time - * (in milliseconds) the call will block waiting - * for metadata to be retrieved. - * Use 0 for non-blocking calls. - - * @remark Requires broker version >=0.10.0 and api.version.request=true. - * - * @returns the controller broker id (>= 0), or -1 if no ControllerId could be - * retrieved in the allotted timespan. - */ -RD_EXPORT -int32_t rd_kafka_controllerid(rd_kafka_t *rk, int timeout_ms); - - -/** - * @brief Creates a new topic handle for topic named \p topic. - * - * \p conf is an optional configuration for the topic created with - * `rd_kafka_topic_conf_new()` that will be used instead of the default - * topic configuration. - * The \p conf object is freed by this function and must not be used or - * destroyed by the application sub-sequently. - * See `rd_kafka_topic_conf_set()` et.al for more information. - * - * Topic handles are refcounted internally and calling rd_kafka_topic_new() - * again with the same topic name will return the previous topic handle - * without updating the original handle's configuration. - * Applications must eventually call rd_kafka_topic_destroy() for each - * succesfull call to rd_kafka_topic_new() to clear up resources. - * - * @returns the new topic handle or NULL on error (use rd_kafka_errno2err() - * to convert system \p errno to an rd_kafka_resp_err_t error code. - * - * @sa rd_kafka_topic_destroy() - */ -RD_EXPORT -rd_kafka_topic_t *rd_kafka_topic_new(rd_kafka_t *rk, - const char *topic, - rd_kafka_topic_conf_t *conf); - - - -/** - * @brief Loose application's topic handle refcount as previously created - * with `rd_kafka_topic_new()`. - * - * @remark Since topic objects are refcounted (both internally and for the app) - * the topic object might not actually be destroyed by this call, - * but the application must consider the object destroyed. - */ -RD_EXPORT -void rd_kafka_topic_destroy(rd_kafka_topic_t *rkt); - - -/** - * @brief Returns the topic name. - */ -RD_EXPORT -const char *rd_kafka_topic_name(const rd_kafka_topic_t *rkt); - - -/** - * @brief Get the \p rkt_opaque pointer that was set in the topic configuration - * with rd_kafka_topic_conf_set_opaque(). - */ -RD_EXPORT -void *rd_kafka_topic_opaque(const rd_kafka_topic_t *rkt); - - -/** - * @brief Unassigned partition. - * - * The unassigned partition is used by the producer API for messages - * that should be partitioned using the configured or default partitioner. - */ -#define RD_KAFKA_PARTITION_UA ((int32_t)-1) - - -/** - * @brief Polls the provided kafka handle for events. - * - * Events will cause application provided callbacks to be called. - * - * The \p timeout_ms argument specifies the maximum amount of time - * (in milliseconds) that the call will block waiting for events. - * For non-blocking calls, provide 0 as \p timeout_ms. - * To wait indefinately for an event, provide -1. - * - * @remark An application should make sure to call poll() at regular - * intervals to serve any queued callbacks waiting to be called. - * @remark If your producer doesn't have any callback set (in particular - * via rd_kafka_conf_set_dr_msg_cb or rd_kafka_conf_set_error_cb) - * you might chose not to call poll(), though this is not - * recommended. - * - * Events: - * - delivery report callbacks (if dr_cb/dr_msg_cb is configured) [producer] - * - error callbacks (rd_kafka_conf_set_error_cb()) [all] - * - stats callbacks (rd_kafka_conf_set_stats_cb()) [all] - * - throttle callbacks (rd_kafka_conf_set_throttle_cb()) [all] - * - OAUTHBEARER token refresh callbacks - * (rd_kafka_conf_set_oauthbearer_token_refresh_cb()) [all] - * - * @returns the number of events served. - */ -RD_EXPORT -int rd_kafka_poll(rd_kafka_t *rk, int timeout_ms); - - -/** - * @brief Cancels the current callback dispatcher (rd_kafka_poll(), - * rd_kafka_consume_callback(), etc). - * - * A callback may use this to force an immediate return to the calling - * code (caller of e.g. rd_kafka_poll()) without processing any further - * events. - * - * @remark This function MUST ONLY be called from within a librdkafka callback. - */ -RD_EXPORT -void rd_kafka_yield(rd_kafka_t *rk); - - - -/** - * @brief Pause producing or consumption for the provided list of partitions. - * - * Success or error is returned per-partition \p err in the \p partitions list. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_pause_partitions(rd_kafka_t *rk, - rd_kafka_topic_partition_list_t *partitions); - - - -/** - * @brief Resume producing consumption for the provided list of partitions. - * - * Success or error is returned per-partition \p err in the \p partitions list. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_resume_partitions(rd_kafka_t *rk, - rd_kafka_topic_partition_list_t *partitions); - - - -/** - * @brief Query broker for low (oldest/beginning) and high (newest/end) offsets - * for partition. - * - * Offsets are returned in \p *low and \p *high respectively. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or an error code on failure. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_query_watermark_offsets(rd_kafka_t *rk, - const char *topic, - int32_t partition, - int64_t *low, - int64_t *high, - int timeout_ms); - - -/** - * @brief Get last known low (oldest/beginning) and high (newest/end) offsets - * for partition. - * - * The low offset is updated periodically (if statistics.interval.ms is set) - * while the high offset is updated on each fetched message set from the broker. - * - * If there is no cached offset (either low or high, or both) then - * RD_KAFKA_OFFSET_INVALID will be returned for the respective offset. - * - * Offsets are returned in \p *low and \p *high respectively. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or an error code on failure. - * - * @remark Shall only be used with an active consumer instance. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_get_watermark_offsets(rd_kafka_t *rk, - const char *topic, - int32_t partition, - int64_t *low, - int64_t *high); - - - -/** - * @brief Look up the offsets for the given partitions by timestamp. - * - * The returned offset for each partition is the earliest offset whose - * timestamp is greater than or equal to the given timestamp in the - * corresponding partition. - * - * The timestamps to query are represented as \c offset in \p offsets - * on input, and \c offset will contain the offset on output. - * - * The function will block for at most \p timeout_ms milliseconds. - * - * @remark Duplicate Topic+Partitions are not supported. - * @remark Per-partition errors may be returned in \c - * rd_kafka_topic_partition_t.err - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR if offsets were be queried (do note - * that per-partition errors might be set), - * RD_KAFKA_RESP_ERR__TIMED_OUT if not all offsets could be fetched - * within \p timeout_ms, - * RD_KAFKA_RESP_ERR__INVALID_ARG if the \p offsets list is empty, - * RD_KAFKA_RESP_ERR__UNKNOWN_PARTITION if all partitions are unknown, - * RD_KAFKA_RESP_ERR_LEADER_NOT_AVAILABLE if unable to query leaders - * for the given partitions. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_offsets_for_times(rd_kafka_t *rk, - rd_kafka_topic_partition_list_t *offsets, - int timeout_ms); - - - -/** - * @brief Allocate and zero memory using the same allocator librdkafka uses. - * - * This is typically an abstraction for the calloc(3) call and makes sure - * the application can use the same memory allocator as librdkafka for - * allocating pointers that are used by librdkafka. - * - * \p rk can be set to return memory allocated by a specific \c rk instance - * otherwise pass NULL for \p rk. - * - * @remark Memory allocated by rd_kafka_mem_calloc() must be freed using - * rd_kafka_mem_free() - */ -RD_EXPORT -void *rd_kafka_mem_calloc(rd_kafka_t *rk, size_t num, size_t size); - - - -/** - * @brief Allocate memory using the same allocator librdkafka uses. - * - * This is typically an abstraction for the malloc(3) call and makes sure - * the application can use the same memory allocator as librdkafka for - * allocating pointers that are used by librdkafka. - * - * \p rk can be set to return memory allocated by a specific \c rk instance - * otherwise pass NULL for \p rk. - * - * @remark Memory allocated by rd_kafka_mem_malloc() must be freed using - * rd_kafka_mem_free() - */ -RD_EXPORT -void *rd_kafka_mem_malloc(rd_kafka_t *rk, size_t size); - - - -/** - * @brief Free pointer returned by librdkafka - * - * This is typically an abstraction for the free(3) call and makes sure - * the application can use the same memory allocator as librdkafka for - * freeing pointers returned by librdkafka. - * - * In standard setups it is usually not necessary to use this interface - * rather than the free(3) functione. - * - * \p rk must be set for memory returned by APIs that take an \c rk argument, - * for other APIs pass NULL for \p rk. - * - * @remark rd_kafka_mem_free() must only be used for pointers returned by APIs - * that explicitly mention using this function for freeing. - */ -RD_EXPORT -void rd_kafka_mem_free(rd_kafka_t *rk, void *ptr); - - -/**@}*/ - - - -/** - * @name Queue API - * @{ - * - * Message queues allows the application to re-route consumed messages - * from multiple topic+partitions into one single queue point. - * This queue point containing messages from a number of topic+partitions - * may then be served by a single rd_kafka_consume*_queue() call, - * rather than one call per topic+partition combination. - */ - - -/** - * @brief Create a new message queue. - * - * See rd_kafka_consume_start_queue(), rd_kafka_consume_queue(), et.al. - */ -RD_EXPORT -rd_kafka_queue_t *rd_kafka_queue_new(rd_kafka_t *rk); - -/** - * Destroy a queue, purging all of its enqueued messages. - */ -RD_EXPORT -void rd_kafka_queue_destroy(rd_kafka_queue_t *rkqu); - - -/** - * @returns a reference to the main librdkafka event queue. - * This is the queue served by rd_kafka_poll(). - * - * Use rd_kafka_queue_destroy() to loose the reference. - */ -RD_EXPORT -rd_kafka_queue_t *rd_kafka_queue_get_main(rd_kafka_t *rk); - - - -/** - * @returns a reference to the SASL callback queue, if a SASL mechanism - * with callbacks is configured (currently only OAUTHBEARER), else - * returns NULL. - * - * Use rd_kafka_queue_destroy() to loose the reference. - * - * @sa rd_kafka_sasl_background_callbacks_enable() - */ -RD_EXPORT -rd_kafka_queue_t *rd_kafka_queue_get_sasl(rd_kafka_t *rk); - - -/** - * @brief Enable SASL OAUTHBEARER refresh callbacks on the librdkafka - * background thread. - * - * This serves as an alternative for applications that do not call - * rd_kafka_poll() (et.al.) at regular intervals (or not at all), as a means - * of automatically trigger the refresh callbacks, which are needed to - * initiate connections to the brokers in the case a custom OAUTHBEARER - * refresh callback is configured. - * - * @returns NULL on success or an error object on error. - * - * @sa rd_kafka_queue_get_sasl() - * @sa rd_kafka_conf_set_oauthbearer_token_refresh_cb() - */ -RD_EXPORT -rd_kafka_error_t *rd_kafka_sasl_background_callbacks_enable(rd_kafka_t *rk); - - -/** - * @returns a reference to the librdkafka consumer queue. - * This is the queue served by rd_kafka_consumer_poll(). - * - * Use rd_kafka_queue_destroy() to loose the reference. - * - * @remark rd_kafka_queue_destroy() MUST be called on this queue - * prior to calling rd_kafka_consumer_close(). - */ -RD_EXPORT -rd_kafka_queue_t *rd_kafka_queue_get_consumer(rd_kafka_t *rk); - -/** - * @returns a reference to the partition's queue, or NULL if - * partition is invalid. - * - * Use rd_kafka_queue_destroy() to loose the reference. - * - * @remark rd_kafka_queue_destroy() MUST be called on this queue - * - * @remark This function only works on consumers. - */ -RD_EXPORT -rd_kafka_queue_t *rd_kafka_queue_get_partition(rd_kafka_t *rk, - const char *topic, - int32_t partition); - -/** - * @returns a reference to the background thread queue, or NULL if the - * background queue is not enabled. - * - * The background thread queue provides the application with an automatically - * polled queue that triggers the event callback in a background thread, - * this background thread is completely managed by librdkafka. - * - * The background thread queue is automatically created if a generic event - * handler callback is configured with rd_kafka_conf_set_background_event_cb() - * or if rd_kafka_queue_get_background() is called. - * - * The background queue is polled and served by librdkafka and MUST NOT be - * polled, forwarded, or otherwise managed by the application, it may only - * be used as the destination queue passed to queue-enabled APIs, such as - * the Admin API. - * - * Use rd_kafka_queue_destroy() to loose the reference. - * - * @warning The background queue MUST NOT be read from (polled, consumed, etc), - * or forwarded from. - */ -RD_EXPORT -rd_kafka_queue_t *rd_kafka_queue_get_background(rd_kafka_t *rk); - - -/** - * @brief Forward/re-route queue \p src to \p dst. - * If \p dst is \c NULL the forwarding is removed. - * - * The internal refcounts for both queues are increased. - * - * @remark Regardless of whether \p dst is NULL or not, after calling this - * function, \p src will not forward it's fetch queue to the consumer - * queue. - */ -RD_EXPORT -void rd_kafka_queue_forward(rd_kafka_queue_t *src, rd_kafka_queue_t *dst); - -/** - * @brief Forward librdkafka logs (and debug) to the specified queue - * for serving with one of the ..poll() calls. - * - * This allows an application to serve log callbacks (\c log_cb) - * in its thread of choice. - * - * @param rk Client instance. - * @param rkqu Queue to forward logs to. If the value is NULL the logs - * are forwarded to the main queue. - * - * @remark The configuration property \c log.queue MUST also be set to true. - * - * @remark librdkafka maintains its own reference to the provided queue. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or an error code on error, - * eg RD_KAFKA_RESP_ERR__NOT_CONFIGURED when log.queue is not set to true. - */ -RD_EXPORT -rd_kafka_resp_err_t rd_kafka_set_log_queue(rd_kafka_t *rk, - rd_kafka_queue_t *rkqu); - - -/** - * @returns the current number of elements in queue. - */ -RD_EXPORT -size_t rd_kafka_queue_length(rd_kafka_queue_t *rkqu); - - -/** - * @brief Enable IO event triggering for queue. - * - * To ease integration with IO based polling loops this API - * allows an application to create a separate file-descriptor - * that librdkafka will write \p payload (of size \p size) to - * whenever a new element is enqueued on a previously empty queue. - * - * To remove event triggering call with \p fd = -1. - * - * librdkafka will maintain a copy of the \p payload. - * - * @remark IO and callback event triggering are mutually exclusive. - * @remark When using forwarded queues the IO event must only be enabled - * on the final forwarded-to (destination) queue. - * @remark The file-descriptor/socket must be set to non-blocking. - */ -RD_EXPORT -void rd_kafka_queue_io_event_enable(rd_kafka_queue_t *rkqu, - int fd, - const void *payload, - size_t size); - -/** - * @brief Enable callback event triggering for queue. - * - * The callback will be called from an internal librdkafka thread - * when a new element is enqueued on a previously empty queue. - * - * To remove event triggering call with \p event_cb = NULL. - * - * The \p qev_opaque is passed to the callback's \p qev_opaque argument. - * - * @remark IO and callback event triggering are mutually exclusive. - * @remark Since the callback may be triggered from internal librdkafka - * threads, the application must not perform any pro-longed work in - * the callback, or call any librdkafka APIs (for the same rd_kafka_t - * handle). - */ -RD_EXPORT -void rd_kafka_queue_cb_event_enable(rd_kafka_queue_t *rkqu, - void (*event_cb)(rd_kafka_t *rk, - void *qev_opaque), - void *qev_opaque); - - -/** - * @brief Cancels the current rd_kafka_queue_poll() on \p rkqu. - * - * An application may use this from another thread to force - * an immediate return to the calling code (caller of rd_kafka_queue_poll()). - * Must not be used from signal handlers since that may cause deadlocks. - */ -RD_EXPORT -void rd_kafka_queue_yield(rd_kafka_queue_t *rkqu); - - -/**@}*/ - -/** - * - * @name Simple Consumer API (legacy) - * @{ - * - */ - - -#define RD_KAFKA_OFFSET_BEGINNING \ - -2 /**< Start consuming from beginning of \ - * kafka partition queue: oldest msg */ -#define RD_KAFKA_OFFSET_END \ - -1 /**< Start consuming from end of kafka \ - * partition queue: next msg */ -#define RD_KAFKA_OFFSET_STORED \ - -1000 /**< Start consuming from offset retrieved \ - * from offset store */ -#define RD_KAFKA_OFFSET_INVALID -1001 /**< Invalid offset */ - - -/** @cond NO_DOC */ -#define RD_KAFKA_OFFSET_TAIL_BASE -2000 /* internal: do not use */ -/** @endcond */ - -/** - * @brief Start consuming \p CNT messages from topic's current end offset. - * - * That is, if current end offset is 12345 and \p CNT is 200, it will start - * consuming from offset \c 12345-200 = \c 12145. */ -#define RD_KAFKA_OFFSET_TAIL(CNT) (RD_KAFKA_OFFSET_TAIL_BASE - (CNT)) - -/** - * @brief Start consuming messages for topic \p rkt and \p partition - * at offset \p offset which may either be an absolute \c (0..N) - * or one of the logical offsets: - * - RD_KAFKA_OFFSET_BEGINNING - * - RD_KAFKA_OFFSET_END - * - RD_KAFKA_OFFSET_STORED - * - RD_KAFKA_OFFSET_TAIL - * - * rdkafka will attempt to keep \c queued.min.messages (config property) - * messages in the local queue by repeatedly fetching batches of messages - * from the broker until the threshold is reached. - * - * The application shall use one of the `rd_kafka_consume*()` functions - * to consume messages from the local queue, each kafka message being - * represented as a `rd_kafka_message_t *` object. - * - * `rd_kafka_consume_start()` must not be called multiple times for the same - * topic and partition without stopping consumption first with - * `rd_kafka_consume_stop()`. - * - * @returns 0 on success or -1 on error in which case errno is set accordingly: - * - EBUSY - Conflicts with an existing or previous subscription - * (RD_KAFKA_RESP_ERR__CONFLICT) - * - EINVAL - Invalid offset, or incomplete configuration (lacking group.id) - * (RD_KAFKA_RESP_ERR__INVALID_ARG) - * - ESRCH - requested \p partition is invalid. - * (RD_KAFKA_RESP_ERR__UNKNOWN_PARTITION) - * - ENOENT - topic is unknown in the Kafka cluster. - * (RD_KAFKA_RESP_ERR__UNKNOWN_TOPIC) - * - * Use `rd_kafka_errno2err()` to convert sytem \c errno to `rd_kafka_resp_err_t` - */ -RD_EXPORT -int rd_kafka_consume_start(rd_kafka_topic_t *rkt, - int32_t partition, - int64_t offset); - -/** - * @brief Same as rd_kafka_consume_start() but re-routes incoming messages to - * the provided queue \p rkqu (which must have been previously allocated - * with `rd_kafka_queue_new()`. - * - * The application must use one of the `rd_kafka_consume_*_queue()` functions - * to receive fetched messages. - * - * `rd_kafka_consume_start_queue()` must not be called multiple times for the - * same topic and partition without stopping consumption first with - * `rd_kafka_consume_stop()`. - * `rd_kafka_consume_start()` and `rd_kafka_consume_start_queue()` must not - * be combined for the same topic and partition. - */ -RD_EXPORT -int rd_kafka_consume_start_queue(rd_kafka_topic_t *rkt, - int32_t partition, - int64_t offset, - rd_kafka_queue_t *rkqu); - -/** - * @brief Stop consuming messages for topic \p rkt and \p partition, purging - * all messages currently in the local queue. - * - * NOTE: To enforce synchronisation this call will block until the internal - * fetcher has terminated and offsets are committed to configured - * storage method. - * - * The application needs to be stop all consumers before calling - * `rd_kafka_destroy()` on the main object handle. - * - * @returns 0 on success or -1 on error (see `errno`). - */ -RD_EXPORT -int rd_kafka_consume_stop(rd_kafka_topic_t *rkt, int32_t partition); - - - -/** - * @brief Seek consumer for topic+partition to \p offset which is either an - * absolute or logical offset. - * - * If \p timeout_ms is specified (not 0) the seek call will wait this long - * for the consumer to update its fetcher state for the given partition with - * the new offset. This guarantees that no previously fetched messages for the - * old offset (or fetch position) will be passed to the application. - * - * If the timeout is reached the internal state will be unknown to the caller - * and this function returns `RD_KAFKA_RESP_ERR__TIMED_OUT`. - * - * If \p timeout_ms is 0 it will initiate the seek but return - * immediately without any error reporting (e.g., async). - * - * This call will purge all pre-fetched messages for the given partition, which - * may be up to \c queued.max.message.kbytes in size. Repeated use of seek - * may thus lead to increased network usage as messages are re-fetched from - * the broker. - * - * @remark Seek must only be performed for already assigned/consumed partitions, - * use rd_kafka_assign() (et.al) to set the initial starting offset - * for a new assignmenmt. - * - * @returns `RD_KAFKA_RESP_ERR__NO_ERROR` on success else an error code. - * - * @deprecated Use rd_kafka_seek_partitions(). - */ -RD_EXPORT -rd_kafka_resp_err_t rd_kafka_seek(rd_kafka_topic_t *rkt, - int32_t partition, - int64_t offset, - int timeout_ms); - - - -/** - * @brief Seek consumer for partitions in \p partitions to the per-partition - * offset in the \c .offset field of \p partitions. - * - * The offset may be either absolute (>= 0) or a logical offset. - * - * If \p timeout_ms is specified (not 0) the seek call will wait this long - * for the consumer to update its fetcher state for the given partition with - * the new offset. This guarantees that no previously fetched messages for the - * old offset (or fetch position) will be passed to the application. - * - * If the timeout is reached the internal state will be unknown to the caller - * and this function returns `RD_KAFKA_RESP_ERR__TIMED_OUT`. - * - * If \p timeout_ms is 0 it will initiate the seek but return - * immediately without any error reporting (e.g., async). - * - * This call will purge all pre-fetched messages for the given partition, which - * may be up to \c queued.max.message.kbytes in size. Repeated use of seek - * may thus lead to increased network usage as messages are re-fetched from - * the broker. - * - * Individual partition errors are reported in the per-partition \c .err field - * of \p partitions. - * - * @remark Seek must only be performed for already assigned/consumed partitions, - * use rd_kafka_assign() (et.al) to set the initial starting offset - * for a new assignmenmt. - * - * @returns NULL on success or an error object on failure. - */ -RD_EXPORT rd_kafka_error_t * -rd_kafka_seek_partitions(rd_kafka_t *rk, - rd_kafka_topic_partition_list_t *partitions, - int timeout_ms); - - -/** - * @brief Consume a single message from topic \p rkt and \p partition - * - * \p timeout_ms is maximum amount of time to wait for a message to be received. - * Consumer must have been previously started with `rd_kafka_consume_start()`. - * - * @returns a message object on success or \c NULL on error. - * The message object must be destroyed with `rd_kafka_message_destroy()` - * when the application is done with it. - * - * Errors (when returning NULL): - * - ETIMEDOUT - \p timeout_ms was reached with no new messages fetched. - * - ENOENT - \p rkt + \p partition is unknown. - * (no prior `rd_kafka_consume_start()` call) - * - * NOTE: The returned message's \c ..->err must be checked for errors. - * NOTE: \c ..->err \c == \c RD_KAFKA_RESP_ERR__PARTITION_EOF signals that the - * end of the partition has been reached, which should typically not be - * considered an error. The application should handle this case - * (e.g., ignore). - * - * @remark on_consume() interceptors may be called from this function prior to - * passing message to application. - */ -RD_EXPORT -rd_kafka_message_t * -rd_kafka_consume(rd_kafka_topic_t *rkt, int32_t partition, int timeout_ms); - - - -/** - * @brief Consume up to \p rkmessages_size from topic \p rkt and \p partition - * putting a pointer to each message in the application provided - * array \p rkmessages (of size \p rkmessages_size entries). - * - * `rd_kafka_consume_batch()` provides higher throughput performance - * than `rd_kafka_consume()`. - * - * \p timeout_ms is the maximum amount of time to wait for all of - * \p rkmessages_size messages to be put into \p rkmessages. - * If no messages were available within the timeout period this function - * returns 0 and \p rkmessages remains untouched. - * This differs somewhat from `rd_kafka_consume()`. - * - * The message objects must be destroyed with `rd_kafka_message_destroy()` - * when the application is done with it. - * - * @returns the number of rkmessages added in \p rkmessages, - * or -1 on error (same error codes as for `rd_kafka_consume()`. - * - * @sa rd_kafka_consume() - * - * @remark on_consume() interceptors may be called from this function prior to - * passing message to application. - */ -RD_EXPORT -ssize_t rd_kafka_consume_batch(rd_kafka_topic_t *rkt, - int32_t partition, - int timeout_ms, - rd_kafka_message_t **rkmessages, - size_t rkmessages_size); - - - -/** - * @brief Consumes messages from topic \p rkt and \p partition, calling - * the provided callback for each consumed messsage. - * - * `rd_kafka_consume_callback()` provides higher throughput performance - * than both `rd_kafka_consume()` and `rd_kafka_consume_batch()`. - * - * \p timeout_ms is the maximum amount of time to wait for one or more messages - * to arrive. - * - * The provided \p consume_cb function is called for each message, - * the application \b MUST \b NOT call `rd_kafka_message_destroy()` on the - * provided \p rkmessage. - * - * The \p commit_opaque argument is passed to the \p consume_cb - * as \p commit_opaque. - * - * @returns the number of messages processed or -1 on error. - * - * @sa rd_kafka_consume() - * - * @remark on_consume() interceptors may be called from this function prior to - * passing message to application. - * - * @remark This function will return early if a transaction control message is - * received, these messages are not exposed to the application but - * still enqueued on the consumer queue to make sure their - * offsets are stored. - * - * @deprecated This API is deprecated and subject for future removal. - * There is no new callback-based consume interface, use the - * poll/queue based alternatives. - */ -RD_EXPORT -int rd_kafka_consume_callback(rd_kafka_topic_t *rkt, - int32_t partition, - int timeout_ms, - void (*consume_cb)(rd_kafka_message_t *rkmessage, - void *commit_opaque), - void *commit_opaque); - - -/** - * @name Simple Consumer API (legacy): Queue consumers - * @{ - * - * The following `..._queue()` functions are analogue to the functions above - * but reads messages from the provided queue \p rkqu instead. - * \p rkqu must have been previously created with `rd_kafka_queue_new()` - * and the topic consumer must have been started with - * `rd_kafka_consume_start_queue()` utilising the the same queue. - */ - -/** - * @brief Consume from queue - * - * @sa rd_kafka_consume() - */ -RD_EXPORT -rd_kafka_message_t *rd_kafka_consume_queue(rd_kafka_queue_t *rkqu, - int timeout_ms); - -/** - * @brief Consume batch of messages from queue - * - * @sa rd_kafka_consume_batch() - */ -RD_EXPORT -ssize_t rd_kafka_consume_batch_queue(rd_kafka_queue_t *rkqu, - int timeout_ms, - rd_kafka_message_t **rkmessages, - size_t rkmessages_size); - -/** - * @brief Consume multiple messages from queue with callback - * - * @sa rd_kafka_consume_callback() - * - * @deprecated This API is deprecated and subject for future removal. - * There is no new callback-based consume interface, use the - * poll/queue based alternatives. - */ -RD_EXPORT -int rd_kafka_consume_callback_queue( - rd_kafka_queue_t *rkqu, - int timeout_ms, - void (*consume_cb)(rd_kafka_message_t *rkmessage, void *commit_opaque), - void *commit_opaque); - - -/**@}*/ - - - -/** - * @name Simple Consumer API (legacy): Topic+partition offset store. - * @{ - * - * If \c auto.commit.enable is true the offset is stored automatically prior to - * returning of the message(s) in each of the rd_kafka_consume*() functions - * above. - */ - - -/** - * @brief Store offset \p offset + 1 for topic \p rkt partition \p partition. - * - * The \c offset + 1 will be committed (written) to broker (or file) according - * to \c `auto.commit.interval.ms` or manual offset-less commit() - * - * @warning This method may only be called for partitions that are currently - * assigned. - * Non-assigned partitions will fail with RD_KAFKA_RESP_ERR__STATE. - * Since v1.9.0. - * - * @warning Avoid storing offsets after calling rd_kafka_seek() (et.al) as - * this may later interfere with resuming a paused partition, instead - * store offsets prior to calling seek. - * - * @remark \c `enable.auto.offset.store` must be set to "false" when using - * this API. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or an error code on error. - */ -RD_EXPORT -rd_kafka_resp_err_t -rd_kafka_offset_store(rd_kafka_topic_t *rkt, int32_t partition, int64_t offset); - - -/** - * @brief Store offsets for next auto-commit for one or more partitions. - * - * The offset will be committed (written) to the offset store according - * to \c `auto.commit.interval.ms` or manual offset-less commit(). - * - * Per-partition success/error status propagated through each partition's - * \c .err for all return values (even NO_ERROR) except INVALID_ARG. - * - * @warning This method may only be called for partitions that are currently - * assigned. - * Non-assigned partitions will fail with RD_KAFKA_RESP_ERR__STATE. - * Since v1.9.0. - * - * @warning Avoid storing offsets after calling rd_kafka_seek() (et.al) as - * this may later interfere with resuming a paused partition, instead - * store offsets prior to calling seek. - * - * @remark The \c .offset field is stored as is, it will NOT be + 1. - * - * @remark \c `enable.auto.offset.store` must be set to "false" when using - * this API. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on (partial) success, or - * RD_KAFKA_RESP_ERR__INVALID_ARG if \c enable.auto.offset.store - * is true, or - * RD_KAFKA_RESP_ERR__UNKNOWN_PARTITION or RD_KAFKA_RESP_ERR__STATE - * if none of the offsets could be stored. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_offsets_store(rd_kafka_t *rk, - rd_kafka_topic_partition_list_t *offsets); -/**@}*/ - - - -/** - * @name KafkaConsumer (C) - * @{ - * @brief High-level KafkaConsumer C API - * - * - * - */ - -/** - * @brief Subscribe to topic set using balanced consumer groups. - * - * Wildcard (regex) topics are supported: - * any topic name in the \p topics list that is prefixed with \c \"^\" will - * be regex-matched to the full list of topics in the cluster and matching - * topics will be added to the subscription list. - * - * The full topic list is retrieved every \c topic.metadata.refresh.interval.ms - * to pick up new or delete topics that match the subscription. - * If there is any change to the matched topics the consumer will - * immediately rejoin the group with the updated set of subscribed topics. - * - * Regex and full topic names can be mixed in \p topics. - * - * @remark Only the \c .topic field is used in the supplied \p topics list, - * all other fields are ignored. - * - * @remark subscribe() is an asynchronous method which returns immediately: - * background threads will (re)join the group, wait for group rebalance, - * issue any registered rebalance_cb, assign() the assigned partitions, - * and then start fetching messages. This cycle may take up to - * \c session.timeout.ms * 2 or more to complete. - * - * @remark After this call returns a consumer error will be returned by - * rd_kafka_consumer_poll (et.al) for each unavailable topic in the - * \p topics. The error will be RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART - * for non-existent topics, and - * RD_KAFKA_RESP_ERR_TOPIC_AUTHORIZATION_FAILED for unauthorized topics. - * The consumer error will be raised through rd_kafka_consumer_poll() - * (et.al.) with the \c rd_kafka_message_t.err field set to one of the - * error codes mentioned above. - * The subscribe function itself is asynchronous and will not return - * an error on unavailable topics. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or - * RD_KAFKA_RESP_ERR__INVALID_ARG if list is empty, contains invalid - * topics or regexes or duplicate entries, - * RD_KAFKA_RESP_ERR__FATAL if the consumer has raised a fatal error. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_subscribe(rd_kafka_t *rk, - const rd_kafka_topic_partition_list_t *topics); - - -/** - * @brief Unsubscribe from the current subscription set. - */ -RD_EXPORT -rd_kafka_resp_err_t rd_kafka_unsubscribe(rd_kafka_t *rk); - - -/** - * @brief Returns the current topic subscription - * - * @returns An error code on failure, otherwise \p topic is updated - * to point to a newly allocated topic list (possibly empty). - * - * @remark The application is responsible for calling - * rd_kafka_topic_partition_list_destroy on the returned list. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_subscription(rd_kafka_t *rk, rd_kafka_topic_partition_list_t **topics); - - - -/** - * @brief Poll the consumer for messages or events. - * - * Will block for at most \p timeout_ms milliseconds. - * - * @remark An application should make sure to call consumer_poll() at regular - * intervals, even if no messages are expected, to serve any - * queued callbacks waiting to be called. This is especially - * important when a rebalance_cb has been registered as it needs - * to be called and handled properly to synchronize internal - * consumer state. - * - * @returns A message object which is a proper message if \p ->err is - * RD_KAFKA_RESP_ERR_NO_ERROR, or an event or error for any other - * value. - * - * @remark on_consume() interceptors may be called from this function prior to - * passing message to application. - * - * @remark When subscribing to topics the application must call poll at - * least every \c max.poll.interval.ms to remain a member of the - * consumer group. - * - * Noteworthy errors returned in \c ->err: - * - RD_KAFKA_RESP_ERR__MAX_POLL_EXCEEDED - application failed to call - * poll within `max.poll.interval.ms`. - * - * @sa rd_kafka_message_t - */ -RD_EXPORT -rd_kafka_message_t *rd_kafka_consumer_poll(rd_kafka_t *rk, int timeout_ms); - -/** - * @brief Close the consumer. - * - * This call will block until the consumer has revoked its assignment, - * calling the \c rebalance_cb if it is configured, committed offsets - * to broker, and left the consumer group (if applicable). - * The maximum blocking time is roughly limited to session.timeout.ms. - * - * @returns An error code indicating if the consumer close was succesful - * or not. - * RD_KAFKA_RESP_ERR__FATAL is returned if the consumer has raised - * a fatal error. - * - * @remark The application still needs to call rd_kafka_destroy() after - * this call finishes to clean up the underlying handle resources. - * - */ -RD_EXPORT -rd_kafka_resp_err_t rd_kafka_consumer_close(rd_kafka_t *rk); - - -/** - * @brief Asynchronously close the consumer. - * - * Performs the same actions as rd_kafka_consumer_close() but in a - * background thread. - * - * Rebalance events/callbacks (etc) will be forwarded to the - * application-provided \p rkqu. The application must poll/serve this queue - * until rd_kafka_consumer_closed() returns true. - * - * @remark Depending on consumer group join state there may or may not be - * rebalance events emitted on \p rkqu. - * - * @returns an error object if the consumer close failed, else NULL. - * - * @sa rd_kafka_consumer_closed() - */ -RD_EXPORT -rd_kafka_error_t *rd_kafka_consumer_close_queue(rd_kafka_t *rk, - rd_kafka_queue_t *rkqu); - - -/** - * @returns 1 if the consumer is closed, else 0. - * - * Should be used in conjunction with rd_kafka_consumer_close_queue() to know - * when the consumer has been closed. - * - * @sa rd_kafka_consumer_close_queue() - */ -RD_EXPORT -int rd_kafka_consumer_closed(rd_kafka_t *rk); - - -/** - * @brief Incrementally add \p partitions to the current assignment. - * - * If a COOPERATIVE assignor (i.e. incremental rebalancing) is being used, - * this method should be used in a rebalance callback to adjust the current - * assignment appropriately in the case where the rebalance type is - * RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS. The application must pass the - * partition list passed to the callback (or a copy of it), even if the - * list is empty. \p partitions must not be NULL. This method may also be - * used outside the context of a rebalance callback. - * - * @returns NULL on success, or an error object if the operation was - * unsuccessful. - * - * @remark The returned error object (if not NULL) must be destroyed with - * rd_kafka_error_destroy(). - */ -RD_EXPORT rd_kafka_error_t * -rd_kafka_incremental_assign(rd_kafka_t *rk, - const rd_kafka_topic_partition_list_t *partitions); - - -/** - * @brief Incrementally remove \p partitions from the current assignment. - * - * If a COOPERATIVE assignor (i.e. incremental rebalancing) is being used, - * this method should be used in a rebalance callback to adjust the current - * assignment appropriately in the case where the rebalance type is - * RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS. The application must pass the - * partition list passed to the callback (or a copy of it), even if the - * list is empty. \p partitions must not be NULL. This method may also be - * used outside the context of a rebalance callback. - * - * @returns NULL on success, or an error object if the operation was - * unsuccessful. - * - * @remark The returned error object (if not NULL) must be destroyed with - * rd_kafka_error_destroy(). - */ -RD_EXPORT rd_kafka_error_t *rd_kafka_incremental_unassign( - rd_kafka_t *rk, - const rd_kafka_topic_partition_list_t *partitions); - - -/** - * @brief The rebalance protocol currently in use. This will be - * "NONE" if the consumer has not (yet) joined a group, else it will - * match the rebalance protocol ("EAGER", "COOPERATIVE") of the - * configured and selected assignor(s). All configured - * assignors must have the same protocol type, meaning - * online migration of a consumer group from using one - * protocol to another (in particular upgading from EAGER - * to COOPERATIVE) without a restart is not currently - * supported. - * - * @returns NULL on error, or one of "NONE", "EAGER", "COOPERATIVE" on success. - */ -RD_EXPORT -const char *rd_kafka_rebalance_protocol(rd_kafka_t *rk); - - -/** - * @brief Atomic assignment of partitions to consume. - * - * The new \p partitions will replace the existing assignment. - * - * A zero-length \p partitions will treat the partitions as a valid, - * albeit empty assignment, and maintain internal state, while a \c NULL - * value for \p partitions will reset and clear the internal state. - * - * When used from a rebalance callback, the application should pass the - * partition list passed to the callback (or a copy of it) even if the list - * is empty (i.e. should not pass NULL in this case) so as to maintain - * internal join state. This is not strictly required - the application - * may adjust the assignment provided by the group. However, this is rarely - * useful in practice. - * - * @returns An error code indicating if the new assignment was applied or not. - * RD_KAFKA_RESP_ERR__FATAL is returned if the consumer has raised - * a fatal error. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_assign(rd_kafka_t *rk, - const rd_kafka_topic_partition_list_t *partitions); - -/** - * @brief Returns the current partition assignment as set by rd_kafka_assign() - * or rd_kafka_incremental_assign(). - * - * @returns An error code on failure, otherwise \p partitions is updated - * to point to a newly allocated partition list (possibly empty). - * - * @remark The application is responsible for calling - * rd_kafka_topic_partition_list_destroy on the returned list. - * - * @remark This assignment represents the partitions assigned through the - * assign functions and not the partitions assigned to this consumer - * instance by the consumer group leader. - * They are usually the same following a rebalance but not necessarily - * since an application is free to assign any partitions. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_assignment(rd_kafka_t *rk, - rd_kafka_topic_partition_list_t **partitions); - - -/** - * @brief Check whether the consumer considers the current assignment to - * have been lost involuntarily. This method is only applicable for - * use with a high level subscribing consumer. Assignments are revoked - * immediately when determined to have been lost, so this method - * is only useful when reacting to a RD_KAFKA_EVENT_REBALANCE event - * or from within a rebalance_cb. Partitions that have been lost may - * already be owned by other members in the group and therefore - * commiting offsets, for example, may fail. - * - * @remark Calling rd_kafka_assign(), rd_kafka_incremental_assign() or - * rd_kafka_incremental_unassign() resets this flag. - * - * @returns Returns 1 if the current partition assignment is considered - * lost, 0 otherwise. - */ -RD_EXPORT int rd_kafka_assignment_lost(rd_kafka_t *rk); - - -/** - * @brief Commit offsets on broker for the provided list of partitions. - * - * \p offsets should contain \c topic, \c partition, \c offset and possibly - * \c metadata. The \c offset should be the offset where consumption will - * resume, i.e., the last processed offset + 1. - * If \p offsets is NULL the current partition assignment will be used instead. - * - * If \p async is false this operation will block until the broker offset commit - * is done, returning the resulting success or error code. - * - * If a rd_kafka_conf_set_offset_commit_cb() offset commit callback has been - * configured the callback will be enqueued for a future call to - * rd_kafka_poll(), rd_kafka_consumer_poll() or similar. - * - * @returns An error code indiciating if the commit was successful, - * or successfully scheduled if asynchronous, or failed. - * RD_KAFKA_RESP_ERR__FATAL is returned if the consumer has raised - * a fatal error. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_commit(rd_kafka_t *rk, - const rd_kafka_topic_partition_list_t *offsets, - int async); - - -/** - * @brief Commit message's offset on broker for the message's partition. - * The committed offset is the message's offset + 1. - * - * @sa rd_kafka_commit - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_commit_message(rd_kafka_t *rk, - const rd_kafka_message_t *rkmessage, - int async); - - -/** - * @brief Commit offsets on broker for the provided list of partitions. - * - * See rd_kafka_commit for \p offsets semantics. - * - * The result of the offset commit will be posted on the provided \p rkqu queue. - * - * If the application uses one of the poll APIs (rd_kafka_poll(), - * rd_kafka_consumer_poll(), rd_kafka_queue_poll(), ..) to serve the queue - * the \p cb callback is required. - * - * The \p commit_opaque argument is passed to the callback as \p commit_opaque, - * or if using the event API the callback is ignored and the offset commit - * result will be returned as an RD_KAFKA_EVENT_COMMIT event and the - * \p commit_opaque value will be available with rd_kafka_event_opaque(). - * - * If \p rkqu is NULL a temporary queue will be created and the callback will - * be served by this call. - * - * @sa rd_kafka_commit() - * @sa rd_kafka_conf_set_offset_commit_cb() - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_commit_queue(rd_kafka_t *rk, - const rd_kafka_topic_partition_list_t *offsets, - rd_kafka_queue_t *rkqu, - void (*cb)(rd_kafka_t *rk, - rd_kafka_resp_err_t err, - rd_kafka_topic_partition_list_t *offsets, - void *commit_opaque), - void *commit_opaque); - - -/** - * @brief Retrieve committed offsets for topics+partitions. - * - * The \p offset field of each requested partition will either be set to - * stored offset or to RD_KAFKA_OFFSET_INVALID in case there was no stored - * offset for that partition. - * - * Committed offsets will be returned according to the `isolation.level` - * configuration property, if set to `read_committed` (default) then only - * stable offsets for fully committed transactions will be returned, while - * `read_uncommitted` may return offsets for not yet committed transactions. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success in which case the - * \p offset or \p err field of each \p partitions' element is filled - * in with the stored offset, or a partition specific error. - * Else returns an error code. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_committed(rd_kafka_t *rk, - rd_kafka_topic_partition_list_t *partitions, - int timeout_ms); - - - -/** - * @brief Retrieve current positions (offsets) for topics+partitions. - * - * The \p offset field of each requested partition will be set to the offset - * of the last consumed message + 1, or RD_KAFKA_OFFSET_INVALID in case there - * was no previous message. - * - * @remark In this context the last consumed message is the offset consumed - * by the current librdkafka instance and, in case of rebalancing, not - * necessarily the last message fetched from the partition. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success in which case the - * \p offset or \p err field of each \p partitions' element is filled - * in with the stored offset, or a partition specific error. - * Else returns an error code. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_position(rd_kafka_t *rk, rd_kafka_topic_partition_list_t *partitions); - - - -/** - * @returns the current consumer group metadata associated with this consumer, - * or NULL if \p rk is not a consumer configured with a \c group.id. - * This metadata object should be passed to the transactional - * producer's rd_kafka_send_offsets_to_transaction() API. - * - * @remark The returned pointer must be freed by the application using - * rd_kafka_consumer_group_metadata_destroy(). - * - * @sa rd_kafka_send_offsets_to_transaction() - */ -RD_EXPORT rd_kafka_consumer_group_metadata_t * -rd_kafka_consumer_group_metadata(rd_kafka_t *rk); - - -/** - * @brief Create a new consumer group metadata object. - * This is typically only used for writing tests. - * - * @param group_id The group id. - * - * @remark The returned pointer must be freed by the application using - * rd_kafka_consumer_group_metadata_destroy(). - */ -RD_EXPORT rd_kafka_consumer_group_metadata_t * -rd_kafka_consumer_group_metadata_new(const char *group_id); - - -/** - * @brief Create a new consumer group metadata object. - * This is typically only used for writing tests. - * - * @param group_id The group id. - * @param generation_id The group generation id. - * @param member_id The group member id. - * @param group_instance_id The group instance id (may be NULL). - * - * @remark The returned pointer must be freed by the application using - * rd_kafka_consumer_group_metadata_destroy(). - */ -RD_EXPORT rd_kafka_consumer_group_metadata_t * -rd_kafka_consumer_group_metadata_new_with_genid(const char *group_id, - int32_t generation_id, - const char *member_id, - const char *group_instance_id); - - -/** - * @brief Frees the consumer group metadata object as returned by - * rd_kafka_consumer_group_metadata(). - */ -RD_EXPORT void -rd_kafka_consumer_group_metadata_destroy(rd_kafka_consumer_group_metadata_t *); - - -/** - * @brief Serialize the consumer group metadata to a binary format. - * This is mainly for client binding use and not for application use. - * - * @remark The serialized metadata format is private and is not compatible - * across different versions or even builds of librdkafka. - * It should only be used in the same process runtime and must only - * be passed to rd_kafka_consumer_group_metadata_read(). - * - * @param cgmd Metadata to be serialized. - * @param bufferp On success this pointer will be updated to point to na - * allocated buffer containing the serialized metadata. - * The buffer must be freed with rd_kafka_mem_free(). - * @param sizep The pointed to size will be updated with the size of - * the serialized buffer. - * - * @returns NULL on success or an error object on failure. - * - * @sa rd_kafka_consumer_group_metadata_read() - */ -RD_EXPORT rd_kafka_error_t *rd_kafka_consumer_group_metadata_write( - const rd_kafka_consumer_group_metadata_t *cgmd, - void **bufferp, - size_t *sizep); - -/** - * @brief Reads serialized consumer group metadata and returns a - * consumer group metadata object. - * This is mainly for client binding use and not for application use. - * - * @remark The serialized metadata format is private and is not compatible - * across different versions or even builds of librdkafka. - * It should only be used in the same process runtime and must only - * be passed to rd_kafka_consumer_group_metadata_read(). - * - * @param cgmdp On success this pointer will be updated to point to a new - * consumer group metadata object which must be freed with - * rd_kafka_consumer_group_metadata_destroy(). - * @param buffer Pointer to the serialized data. - * @param size Size of the serialized data. - * - * @returns NULL on success or an error object on failure. - * - * @sa rd_kafka_consumer_group_metadata_write() - */ -RD_EXPORT rd_kafka_error_t *rd_kafka_consumer_group_metadata_read( - rd_kafka_consumer_group_metadata_t **cgmdp, - const void *buffer, - size_t size); - -/**@}*/ - - - -/** - * @name Producer API - * @{ - * - * - */ - - -/** - * @brief Producer message flags - */ -#define RD_KAFKA_MSG_F_FREE \ - 0x1 /**< Delegate freeing of payload to rdkafka. \ - */ -#define RD_KAFKA_MSG_F_COPY \ - 0x2 /**< rdkafka will make a copy of the payload. \ - */ -#define RD_KAFKA_MSG_F_BLOCK \ - 0x4 /**< Block produce*() on message queue full. \ - * WARNING: If a delivery report callback \ - * is used the application MUST \ - * call rd_kafka_poll() (or equiv.) \ - * to make sure delivered messages \ - * are drained from the internal \ - * delivery report queue. \ - * Failure to do so will result \ - * in indefinately blocking on \ - * the produce() call when the \ - * message queue is full. */ -#define RD_KAFKA_MSG_F_PARTITION \ - 0x8 /**< produce_batch() will honor \ - * per-message partition. */ - - - -/** - * @brief Produce and send a single message to broker. - * - * \p rkt is the target topic which must have been previously created with - * `rd_kafka_topic_new()`. - * - * `rd_kafka_produce()` is an asynch non-blocking API. - * See `rd_kafka_conf_set_dr_msg_cb` on how to setup a callback to be called - * once the delivery status (success or failure) is known. The delivery report - * is trigged by the application calling `rd_kafka_poll()` (at regular - * intervals) or `rd_kafka_flush()` (at termination). - * - * Since producing is asynchronous, you should call `rd_kafka_flush()` before - * you destroy the producer. Otherwise, any outstanding messages will be - * silently discarded. - * - * When temporary errors occur, librdkafka automatically retries to produce the - * messages. Retries are triggered after retry.backoff.ms and when the - * leader broker for the given partition is available. Otherwise, librdkafka - * falls back to polling the topic metadata to monitor when a new leader is - * elected (see the topic.metadata.refresh.fast.interval.ms and - * topic.metadata.refresh.interval.ms configurations) and then performs a - * retry. A delivery error will occur if the message could not be produced - * within message.timeout.ms. - * - * See the "Message reliability" chapter in INTRODUCTION.md for more - * information. - * - * \p partition is the target partition, either: - * - RD_KAFKA_PARTITION_UA (unassigned) for - * automatic partitioning using the topic's partitioner function, or - * - a fixed partition (0..N) - * - * \p msgflags is zero or more of the following flags OR:ed together: - * RD_KAFKA_MSG_F_BLOCK - block \p produce*() call if - * \p queue.buffering.max.messages or - * \p queue.buffering.max.kbytes are exceeded. - * Messages are considered in-queue from the point - * they are accepted by produce() until their corresponding delivery report - * callback/event returns. It is thus a requirement to call rd_kafka_poll() (or - * equiv.) from a separate thread when F_BLOCK is used. See WARNING on \c - * RD_KAFKA_MSG_F_BLOCK above. - * - * RD_KAFKA_MSG_F_FREE - rdkafka will free(3) \p payload when it is done - * with it. - * RD_KAFKA_MSG_F_COPY - the \p payload data will be copied and the - * \p payload pointer will not be used by rdkafka - * after the call returns. - * RD_KAFKA_MSG_F_PARTITION - produce_batch() will honour per-message - * partition, either set manually or by the - * configured partitioner. - * - * .._F_FREE and .._F_COPY are mutually exclusive. If neither of these are - * set, the caller must ensure that the memory backing \p payload remains - * valid and is not modified or reused until the delivery callback is - * invoked. Other buffers passed to `rd_kafka_produce()` don't have this - * restriction on reuse, i.e. the memory backing the key or the topic name - * may be reused as soon as `rd_kafka_produce()` returns. - * - * If the function returns -1 and RD_KAFKA_MSG_F_FREE was specified, then - * the memory associated with the payload is still the caller's - * responsibility. - * - * \p payload is the message payload of size \p len bytes. - * - * \p key is an optional message key of size \p keylen bytes, if non-NULL it - * will be passed to the topic partitioner as well as be sent with the - * message to the broker and passed on to the consumer. - * - * \p msg_opaque is an optional application-provided per-message opaque - * pointer that will provided in the message's delivery report callback - * (\c dr_msg_cb or \c dr_cb) and the \c rd_kafka_message_t \c _private field. - * - * @remark on_send() and on_acknowledgement() interceptors may be called - * from this function. on_acknowledgement() will only be called if the - * message fails partitioning. - * - * @remark If the producer is transactional (\c transactional.id is configured) - * producing is only allowed during an on-going transaction, namely - * after rd_kafka_begin_transaction() has been called. - * - * @returns 0 on success or -1 on error in which case errno is set accordingly: - * - ENOBUFS - maximum number of outstanding messages has been reached: - * "queue.buffering.max.messages" - * (RD_KAFKA_RESP_ERR__QUEUE_FULL) - * - EMSGSIZE - message is larger than configured max size: - * "messages.max.bytes". - * (RD_KAFKA_RESP_ERR_MSG_SIZE_TOO_LARGE) - * - ESRCH - requested \p partition is unknown in the Kafka cluster. - * (RD_KAFKA_RESP_ERR__UNKNOWN_PARTITION) - * - ENOENT - topic is unknown in the Kafka cluster. - * (RD_KAFKA_RESP_ERR__UNKNOWN_TOPIC) - * - ECANCELED - fatal error has been raised on producer, see - * rd_kafka_fatal_error(), - * (RD_KAFKA_RESP_ERR__FATAL). - * - ENOEXEC - transactional state forbids producing - * (RD_KAFKA_RESP_ERR__STATE) - * - * @sa Use rd_kafka_errno2err() to convert `errno` to rdkafka error code. - */ -RD_EXPORT -int rd_kafka_produce(rd_kafka_topic_t *rkt, - int32_t partition, - int msgflags, - void *payload, - size_t len, - const void *key, - size_t keylen, - void *msg_opaque); - - -/** - * @brief Produce and send a single message to broker. - * - * The message is defined by a va-arg list using \c rd_kafka_vtype_t - * tag tuples which must be terminated with a single \c RD_KAFKA_V_END. - * - * @returns \c RD_KAFKA_RESP_ERR_NO_ERROR on success, else an error code as - * described in rd_kafka_produce(). - * \c RD_KAFKA_RESP_ERR__CONFLICT is returned if _V_HEADER and - * _V_HEADERS are mixed. - * - * @sa rd_kafka_produce, rd_kafka_produceva, RD_KAFKA_V_END - */ -RD_EXPORT -rd_kafka_resp_err_t rd_kafka_producev(rd_kafka_t *rk, ...); - - -/** - * @brief Produce and send a single message to broker. - * - * The message is defined by an array of \c rd_kafka_vu_t of - * count \p cnt. - * - * @returns an error object on failure or NULL on success. - * See rd_kafka_producev() for specific error codes. - * - * @sa rd_kafka_produce, rd_kafka_producev, RD_KAFKA_V_END - */ -RD_EXPORT -rd_kafka_error_t * -rd_kafka_produceva(rd_kafka_t *rk, const rd_kafka_vu_t *vus, size_t cnt); - - -/** - * @brief Produce multiple messages. - * - * If partition is RD_KAFKA_PARTITION_UA the configured partitioner will - * be run for each message (slower), otherwise the messages will be enqueued - * to the specified partition directly (faster). - * - * The messages are provided in the array \p rkmessages of count \p message_cnt - * elements. - * The \p partition and \p msgflags are used for all provided messages. - * - * Honoured \p rkmessages[] fields are: - * - payload,len Message payload and length - * - key,key_len Optional message key - * - _private Message opaque pointer (msg_opaque) - * - err Will be set according to success or failure, see - * rd_kafka_produce() for possible error codes. - * Application only needs to check for errors if - * return value != \p message_cnt. - * - * @remark If \c RD_KAFKA_MSG_F_PARTITION is set in \p msgflags, the - * \c .partition field of the \p rkmessages is used instead of - * \p partition. - * - * @returns the number of messages succesfully enqueued for producing. - * - * @remark This interface does NOT support setting message headers on - * the provided \p rkmessages. - */ -RD_EXPORT -int rd_kafka_produce_batch(rd_kafka_topic_t *rkt, - int32_t partition, - int msgflags, - rd_kafka_message_t *rkmessages, - int message_cnt); - - - -/** - * @brief Wait until all outstanding produce requests, et.al, are completed. - * This should typically be done prior to destroying a producer instance - * to make sure all queued and in-flight produce requests are completed - * before terminating. - * - * @remark This function will call rd_kafka_poll() and thus trigger callbacks. - * - * @remark The \c linger.ms time will be ignored for the duration of the call, - * queued messages will be sent to the broker as soon as possible. - * - * @remark If RD_KAFKA_EVENT_DR has been enabled - * (through rd_kafka_conf_set_events()) this function will not call - * rd_kafka_poll() but instead wait for the librdkafka-handled - * message count to reach zero. This requires the application to - * serve the event queue in a separate thread. - * In this mode only messages are counted, not other types of - * queued events. - * - * @returns RD_KAFKA_RESP_ERR__TIMED_OUT if \p timeout_ms was reached before all - * outstanding requests were completed, else RD_KAFKA_RESP_ERR_NO_ERROR - * - * @sa rd_kafka_outq_len() - */ -RD_EXPORT -rd_kafka_resp_err_t rd_kafka_flush(rd_kafka_t *rk, int timeout_ms); - - - -/** - * @brief Purge messages currently handled by the producer instance. - * - * @param rk Client instance. - * @param purge_flags Tells which messages to purge and how. - * - * The application will need to call rd_kafka_poll() or rd_kafka_flush() - * afterwards to serve the delivery report callbacks of the purged messages. - * - * Messages purged from internal queues fail with the delivery report - * error code set to RD_KAFKA_RESP_ERR__PURGE_QUEUE, while purged messages that - * are in-flight to or from the broker will fail with the error code set to - * RD_KAFKA_RESP_ERR__PURGE_INFLIGHT. - * - * @warning Purging messages that are in-flight to or from the broker - * will ignore any sub-sequent acknowledgement for these messages - * received from the broker, effectively making it impossible - * for the application to know if the messages were successfully - * produced or not. This may result in duplicate messages if the - * application retries these messages at a later time. - * - * @remark This call may block for a short time while background thread - * queues are purged. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success, - * RD_KAFKA_RESP_ERR__INVALID_ARG if the \p purge flags are invalid - * or unknown, - * RD_KAFKA_RESP_ERR__NOT_IMPLEMENTED if called on a non-producer - * client instance. - */ -RD_EXPORT -rd_kafka_resp_err_t rd_kafka_purge(rd_kafka_t *rk, int purge_flags); - - -/** - * @brief Flags for rd_kafka_purge() - */ - -/*! - * Purge messages in internal queues. - */ -#define RD_KAFKA_PURGE_F_QUEUE 0x1 - -/*! - * Purge messages in-flight to or from the broker. - * Purging these messages will void any future acknowledgements from the - * broker, making it impossible for the application to know if these - * messages were successfully delivered or not. - * Retrying these messages may lead to duplicates. - */ -#define RD_KAFKA_PURGE_F_INFLIGHT 0x2 - - -/*! - * Don't wait for background thread queue purging to finish. - */ -#define RD_KAFKA_PURGE_F_NON_BLOCKING 0x4 - - -/**@}*/ - - -/** - * @name Metadata API - * @{ - * - * - */ - - -/** - * @brief Broker information - */ -typedef struct rd_kafka_metadata_broker { - int32_t id; /**< Broker Id */ - char *host; /**< Broker hostname */ - int port; /**< Broker listening port */ -} rd_kafka_metadata_broker_t; - -/** - * @brief Partition information - */ -typedef struct rd_kafka_metadata_partition { - int32_t id; /**< Partition Id */ - rd_kafka_resp_err_t err; /**< Partition error reported by broker */ - int32_t leader; /**< Leader broker */ - int replica_cnt; /**< Number of brokers in \p replicas */ - int32_t *replicas; /**< Replica brokers */ - int isr_cnt; /**< Number of ISR brokers in \p isrs */ - int32_t *isrs; /**< In-Sync-Replica brokers */ -} rd_kafka_metadata_partition_t; - -/** - * @brief Topic information - */ -typedef struct rd_kafka_metadata_topic { - char *topic; /**< Topic name */ - int partition_cnt; /**< Number of partitions in \p partitions*/ - struct rd_kafka_metadata_partition *partitions; /**< Partitions */ - rd_kafka_resp_err_t err; /**< Topic error reported by broker */ -} rd_kafka_metadata_topic_t; - - -/** - * @brief Metadata container - */ -typedef struct rd_kafka_metadata { - int broker_cnt; /**< Number of brokers in \p brokers */ - struct rd_kafka_metadata_broker *brokers; /**< Brokers */ - - int topic_cnt; /**< Number of topics in \p topics */ - struct rd_kafka_metadata_topic *topics; /**< Topics */ - - int32_t orig_broker_id; /**< Broker originating this metadata */ - char *orig_broker_name; /**< Name of originating broker */ -} rd_kafka_metadata_t; - - -/** - * @brief Request Metadata from broker. - * - * Parameters: - * - \p all_topics if non-zero: request info about all topics in cluster, - * if zero: only request info about locally known topics. - * - \p only_rkt only request info about this topic - * - \p metadatap pointer to hold metadata result. - * The \p *metadatap pointer must be released - * with rd_kafka_metadata_destroy(). - * - \p timeout_ms maximum response time before failing. - * - * @remark Consumer: If \p all_topics is non-zero the Metadata response - * information may trigger a re-join if any subscribed topics - * have changed partition count or existence state. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success (in which case *metadatap) - * will be set, else RD_KAFKA_RESP_ERR__TIMED_OUT on timeout or - * other error code on error. - */ -RD_EXPORT -rd_kafka_resp_err_t -rd_kafka_metadata(rd_kafka_t *rk, - int all_topics, - rd_kafka_topic_t *only_rkt, - const struct rd_kafka_metadata **metadatap, - int timeout_ms); - -/** - * @brief Release metadata memory. - */ -RD_EXPORT -void rd_kafka_metadata_destroy(const struct rd_kafka_metadata *metadata); - - -/**@}*/ - - - -/** - * @name Client group information - * @{ - * - * - */ - - -/** - * @brief Group member information - * - * For more information on \p member_metadata format, see - * https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol#AGuideToTheKafkaProtocol-GroupMembershipAPI - * - */ -struct rd_kafka_group_member_info { - char *member_id; /**< Member id (generated by broker) */ - char *client_id; /**< Client's \p client.id */ - char *client_host; /**< Client's hostname */ - void *member_metadata; /**< Member metadata (binary), - * format depends on \p protocol_type. */ - int member_metadata_size; /**< Member metadata size in bytes */ - void *member_assignment; /**< Member assignment (binary), - * format depends on \p protocol_type. */ - int member_assignment_size; /**< Member assignment size in bytes */ -}; - -/** - * @brief Group information - */ -struct rd_kafka_group_info { - struct rd_kafka_metadata_broker broker; /**< Originating broker info */ - char *group; /**< Group name */ - rd_kafka_resp_err_t err; /**< Broker-originated error */ - char *state; /**< Group state */ - char *protocol_type; /**< Group protocol type */ - char *protocol; /**< Group protocol */ - struct rd_kafka_group_member_info *members; /**< Group members */ - int member_cnt; /**< Group member count */ -}; - -/** - * @brief List of groups - * - * @sa rd_kafka_group_list_destroy() to release list memory. - */ -struct rd_kafka_group_list { - struct rd_kafka_group_info *groups; /**< Groups */ - int group_cnt; /**< Group count */ -}; - - -/** - * @brief List and describe client groups in cluster. - * - * \p group is an optional group name to describe, otherwise (\p NULL) all - * groups are returned. - * - * \p timeout_ms is the (approximate) maximum time to wait for response - * from brokers and must be a positive value. - * - * @returns \c RD_KAFKA_RESP_ERR__NO_ERROR on success and \p grplistp is - * updated to point to a newly allocated list of groups. - * \c RD_KAFKA_RESP_ERR__PARTIAL if not all brokers responded - * in time but at least one group is returned in \p grplistlp. - * \c RD_KAFKA_RESP_ERR__TIMED_OUT if no groups were returned in the - * given timeframe but not all brokers have yet responded, or - * if the list of brokers in the cluster could not be obtained within - * the given timeframe. - * \c RD_KAFKA_RESP_ERR__TRANSPORT if no brokers were found. - * Other error codes may also be returned from the request layer. - * - * The \p grplistp remains untouched if any error code is returned, - * with the exception of RD_KAFKA_RESP_ERR__PARTIAL which behaves - * as RD_KAFKA_RESP_ERR__NO_ERROR (success) but with an incomplete - * group list. - * - * @sa Use rd_kafka_group_list_destroy() to release list memory. - */ -RD_EXPORT -rd_kafka_resp_err_t -rd_kafka_list_groups(rd_kafka_t *rk, - const char *group, - const struct rd_kafka_group_list **grplistp, - int timeout_ms); - -/** - * @brief Release list memory - */ -RD_EXPORT -void rd_kafka_group_list_destroy(const struct rd_kafka_group_list *grplist); - - -/**@}*/ - - - -/** - * @name Miscellaneous APIs - * @{ - * - */ - - -/** - * @brief Adds one or more brokers to the kafka handle's list of initial - * bootstrap brokers. - * - * Additional brokers will be discovered automatically as soon as rdkafka - * connects to a broker by querying the broker metadata. - * - * If a broker name resolves to multiple addresses (and possibly - * address families) all will be used for connection attempts in - * round-robin fashion. - * - * \p brokerlist is a ,-separated list of brokers in the format: - * \c \,\,.. - * Where each broker is in either the host or URL based format: - * \c \[:\] - * \c \://\[:port] - * \c \ is either \c PLAINTEXT, \c SSL, \c SASL, \c SASL_PLAINTEXT - * The two formats can be mixed but ultimately the value of the - * `security.protocol` config property decides what brokers are allowed. - * - * Example: - * brokerlist = "broker1:10000,broker2" - * brokerlist = "SSL://broker3:9000,ssl://broker2" - * - * @returns the number of brokers successfully added. - * - * @remark Brokers may also be defined with the \c metadata.broker.list or - * \c bootstrap.servers configuration property (preferred method). - * - * @deprecated Set bootstrap servers with the \c bootstrap.servers - * configuration property. - */ -RD_EXPORT -int rd_kafka_brokers_add(rd_kafka_t *rk, const char *brokerlist); - - - -/** - * @brief Set logger function. - * - * The default is to print to stderr, but a syslog logger is also available, - * see rd_kafka_log_(print|syslog) for the builtin alternatives. - * Alternatively the application may provide its own logger callback. - * Or pass 'func' as NULL to disable logging. - * - * @deprecated Use rd_kafka_conf_set_log_cb() - * - * @remark \p rk may be passed as NULL in the callback. - */ -RD_EXPORT RD_DEPRECATED void -rd_kafka_set_logger(rd_kafka_t *rk, - void (*func)(const rd_kafka_t *rk, - int level, - const char *fac, - const char *buf)); - - -/** - * @brief Specifies the maximum logging level emitted by - * internal kafka logging and debugging. - * - * @deprecated Set the \c "log_level" configuration property instead. - * - * @remark If the \p \"debug\" configuration property is set the log level is - * automatically adjusted to \c LOG_DEBUG (7). - */ -RD_EXPORT -void rd_kafka_set_log_level(rd_kafka_t *rk, int level); - - -/** - * @brief Builtin (default) log sink: print to stderr - */ -RD_EXPORT -void rd_kafka_log_print(const rd_kafka_t *rk, - int level, - const char *fac, - const char *buf); - - -/** - * @brief Builtin log sink: print to syslog. - * @remark This logger is only available if librdkafka was built - * with syslog support. - */ -RD_EXPORT -void rd_kafka_log_syslog(const rd_kafka_t *rk, - int level, - const char *fac, - const char *buf); - - -/** - * @brief Returns the current out queue length. - * - * The out queue length is the sum of: - * - number of messages waiting to be sent to, or acknowledged by, - * the broker. - * - number of delivery reports (e.g., dr_msg_cb) waiting to be served - * by rd_kafka_poll() or rd_kafka_flush(). - * - number of callbacks (e.g., error_cb, stats_cb, etc) waiting to be - * served by rd_kafka_poll(), rd_kafka_consumer_poll() or rd_kafka_flush(). - * - number of events waiting to be served by background_event_cb() in - * the background queue (see rd_kafka_conf_set_background_event_cb). - * - * An application should wait for the return value of this function to reach - * zero before terminating to make sure outstanding messages, - * requests (such as offset commits), callbacks and events are fully processed. - * See rd_kafka_flush(). - * - * @returns number of messages and events waiting in queues. - * - * @sa rd_kafka_flush() - */ -RD_EXPORT -int rd_kafka_outq_len(rd_kafka_t *rk); - - - -/** - * @brief Dumps rdkafka's internal state for handle \p rk to stream \p fp - * - * This is only useful for debugging rdkafka, showing state and statistics - * for brokers, topics, partitions, etc. - */ -RD_EXPORT -void rd_kafka_dump(FILE *fp, rd_kafka_t *rk); - - - -/** - * @brief Retrieve the current number of threads in use by librdkafka. - * - * Used by regression tests. - */ -RD_EXPORT -int rd_kafka_thread_cnt(void); - - -/** - * @enum rd_kafka_thread_type_t - * - * @brief librdkafka internal thread type. - * - * @sa rd_kafka_interceptor_add_on_thread_start() - */ -typedef enum rd_kafka_thread_type_t { - RD_KAFKA_THREAD_MAIN, /**< librdkafka's internal main thread */ - RD_KAFKA_THREAD_BACKGROUND, /**< Background thread (if enabled) */ - RD_KAFKA_THREAD_BROKER /**< Per-broker thread */ -} rd_kafka_thread_type_t; - - -/** - * @brief Wait for all rd_kafka_t objects to be destroyed. - * - * Returns 0 if all kafka objects are now destroyed, or -1 if the - * timeout was reached. - * - * @remark This function is deprecated. - */ -RD_EXPORT -int rd_kafka_wait_destroyed(int timeout_ms); - - -/** - * @brief Run librdkafka's built-in unit-tests. - * - * @returns the number of failures, or 0 if all tests passed. - */ -RD_EXPORT -int rd_kafka_unittest(void); - - -/**@}*/ - - - -/** - * @name Experimental APIs - * @{ - */ - -/** - * @brief Redirect the main (rd_kafka_poll()) queue to the KafkaConsumer's - * queue (rd_kafka_consumer_poll()). - * - * @warning It is not permitted to call rd_kafka_poll() after directing the - * main queue with rd_kafka_poll_set_consumer(). - */ -RD_EXPORT -rd_kafka_resp_err_t rd_kafka_poll_set_consumer(rd_kafka_t *rk); - - -/**@}*/ - -/** - * @name Event interface - * - * @brief The event API provides an alternative pollable non-callback interface - * to librdkafka's message and event queues. - * - * @{ - */ - - -/** - * @brief Event types - */ -typedef int rd_kafka_event_type_t; -#define RD_KAFKA_EVENT_NONE 0x0 /**< Unset value */ -#define RD_KAFKA_EVENT_DR 0x1 /**< Producer Delivery report batch */ -#define RD_KAFKA_EVENT_FETCH 0x2 /**< Fetched message (consumer) */ -#define RD_KAFKA_EVENT_LOG 0x4 /**< Log message */ -#define RD_KAFKA_EVENT_ERROR 0x8 /**< Error */ -#define RD_KAFKA_EVENT_REBALANCE 0x10 /**< Group rebalance (consumer) */ -#define RD_KAFKA_EVENT_OFFSET_COMMIT 0x20 /**< Offset commit result */ -#define RD_KAFKA_EVENT_STATS 0x40 /**< Stats */ -#define RD_KAFKA_EVENT_CREATETOPICS_RESULT 100 /**< CreateTopics_result_t */ -#define RD_KAFKA_EVENT_DELETETOPICS_RESULT 101 /**< DeleteTopics_result_t */ -#define RD_KAFKA_EVENT_CREATEPARTITIONS_RESULT \ - 102 /**< CreatePartitions_result_t */ -#define RD_KAFKA_EVENT_ALTERCONFIGS_RESULT 103 /**< AlterConfigs_result_t */ -#define RD_KAFKA_EVENT_DESCRIBECONFIGS_RESULT \ - 104 /**< DescribeConfigs_result_t */ -#define RD_KAFKA_EVENT_DELETERECORDS_RESULT 105 /**< DeleteRecords_result_t */ -#define RD_KAFKA_EVENT_DELETEGROUPS_RESULT 106 /**< DeleteGroups_result_t */ -/** DeleteConsumerGroupOffsets_result_t */ -#define RD_KAFKA_EVENT_DELETECONSUMERGROUPOFFSETS_RESULT 107 -/** SASL/OAUTHBEARER token needs to be refreshed */ -#define RD_KAFKA_EVENT_OAUTHBEARER_TOKEN_REFRESH 0x100 -#define RD_KAFKA_EVENT_BACKGROUND 0x200 /**< Enable background thread. */ -#define RD_KAFKA_EVENT_CREATEACLS_RESULT 0x400 /**< CreateAcls_result_t */ -#define RD_KAFKA_EVENT_DESCRIBEACLS_RESULT 0x800 /**< DescribeAcls_result_t */ -#define RD_KAFKA_EVENT_DELETEACLS_RESULT 0x1000 /**< DeleteAcls_result_t */ - -/** - * @returns the event type for the given event. - * - * @remark As a convenience it is okay to pass \p rkev as NULL in which case - * RD_KAFKA_EVENT_NONE is returned. - */ -RD_EXPORT -rd_kafka_event_type_t rd_kafka_event_type(const rd_kafka_event_t *rkev); - -/** - * @returns the event type's name for the given event. - * - * @remark As a convenience it is okay to pass \p rkev as NULL in which case - * the name for RD_KAFKA_EVENT_NONE is returned. - */ -RD_EXPORT -const char *rd_kafka_event_name(const rd_kafka_event_t *rkev); - - -/** - * @brief Destroy an event. - * - * @remark Any references to this event, such as extracted messages, - * will not be usable after this call. - * - * @remark As a convenience it is okay to pass \p rkev as NULL in which case - * no action is performed. - */ -RD_EXPORT -void rd_kafka_event_destroy(rd_kafka_event_t *rkev); - - -/** - * @returns the next message from an event. - * - * Call repeatedly until it returns NULL. - * - * Event types: - * - RD_KAFKA_EVENT_FETCH (1 message) - * - RD_KAFKA_EVENT_DR (>=1 message(s)) - * - * @remark The returned message(s) MUST NOT be - * freed with rd_kafka_message_destroy(). - * - * @remark on_consume() interceptor may be called - * from this function prior to passing message to application. - */ -RD_EXPORT -const rd_kafka_message_t *rd_kafka_event_message_next(rd_kafka_event_t *rkev); - - -/** - * @brief Extacts \p size message(s) from the event into the - * pre-allocated array \p rkmessages. - * - * Event types: - * - RD_KAFKA_EVENT_FETCH (1 message) - * - RD_KAFKA_EVENT_DR (>=1 message(s)) - * - * @returns the number of messages extracted. - * - * @remark on_consume() interceptor may be called - * from this function prior to passing message to application. - */ -RD_EXPORT -size_t rd_kafka_event_message_array(rd_kafka_event_t *rkev, - const rd_kafka_message_t **rkmessages, - size_t size); - - -/** - * @returns the number of remaining messages in the event. - * - * Event types: - * - RD_KAFKA_EVENT_FETCH (1 message) - * - RD_KAFKA_EVENT_DR (>=1 message(s)) - */ -RD_EXPORT -size_t rd_kafka_event_message_count(rd_kafka_event_t *rkev); - - -/** - * @returns the associated configuration string for the event, or NULL - * if the configuration property is not set or if - * not applicable for the given event type. - * - * The returned memory is read-only and its lifetime is the same as the - * event object. - * - * Event types: - * - RD_KAFKA_EVENT_OAUTHBEARER_TOKEN_REFRESH: value of sasl.oauthbearer.config - */ -RD_EXPORT -const char *rd_kafka_event_config_string(rd_kafka_event_t *rkev); - - -/** - * @returns the error code for the event. - * - * Use rd_kafka_event_error_is_fatal() to detect if this is a fatal error. - * - * Event types: - * - all - */ -RD_EXPORT -rd_kafka_resp_err_t rd_kafka_event_error(rd_kafka_event_t *rkev); - - -/** - * @returns the error string (if any). - * An application should check that rd_kafka_event_error() returns - * non-zero before calling this function. - * - * Event types: - * - all - */ -RD_EXPORT -const char *rd_kafka_event_error_string(rd_kafka_event_t *rkev); - - -/** - * @returns 1 if the error is a fatal error, else 0. - * - * Event types: - * - RD_KAFKA_EVENT_ERROR - * - * @sa rd_kafka_fatal_error() - */ -RD_EXPORT -int rd_kafka_event_error_is_fatal(rd_kafka_event_t *rkev); - - -/** - * @returns the event opaque (if any) as passed to rd_kafka_commit() (et.al) or - * rd_kafka_AdminOptions_set_opaque(), depending on event type. - * - * Event types: - * - RD_KAFKA_EVENT_OFFSET_COMMIT - * - RD_KAFKA_EVENT_CREATETOPICS_RESULT - * - RD_KAFKA_EVENT_DELETETOPICS_RESULT - * - RD_KAFKA_EVENT_CREATEPARTITIONS_RESULT - * - RD_KAFKA_EVENT_CREATEACLS_RESULT - * - RD_KAFKA_EVENT_DESCRIBEACLS_RESULT - * - RD_KAFKA_EVENT_DELETEACLS_RESULT - * - RD_KAFKA_EVENT_ALTERCONFIGS_RESULT - * - RD_KAFKA_EVENT_DESCRIBECONFIGS_RESULT - * - RD_KAFKA_EVENT_DELETEGROUPS_RESULT - * - RD_KAFKA_EVENT_DELETECONSUMERGROUPOFFSETS_RESULT - * - RD_KAFKA_EVENT_DELETERECORDS_RESULT - */ -RD_EXPORT -void *rd_kafka_event_opaque(rd_kafka_event_t *rkev); - - -/** - * @brief Extract log message from the event. - * - * Event types: - * - RD_KAFKA_EVENT_LOG - * - * @returns 0 on success or -1 if unsupported event type. - */ -RD_EXPORT -int rd_kafka_event_log(rd_kafka_event_t *rkev, - const char **fac, - const char **str, - int *level); - - -/** - * @brief Extract log debug context from event. - * - * Event types: - * - RD_KAFKA_EVENT_LOG - * - * @param rkev the event to extract data from. - * @param dst destination string for comma separated list. - * @param dstsize size of provided dst buffer. - * @returns 0 on success or -1 if unsupported event type. - */ -RD_EXPORT -int rd_kafka_event_debug_contexts(rd_kafka_event_t *rkev, - char *dst, - size_t dstsize); - - -/** - * @brief Extract stats from the event. - * - * Event types: - * - RD_KAFKA_EVENT_STATS - * - * @returns stats json string. - * - * @remark the returned string will be freed automatically along with the event - * object - * - */ -RD_EXPORT -const char *rd_kafka_event_stats(rd_kafka_event_t *rkev); - - -/** - * @returns the topic partition list from the event. - * - * @remark The list MUST NOT be freed with - * rd_kafka_topic_partition_list_destroy() - * - * Event types: - * - RD_KAFKA_EVENT_REBALANCE - * - RD_KAFKA_EVENT_OFFSET_COMMIT - */ -RD_EXPORT rd_kafka_topic_partition_list_t * -rd_kafka_event_topic_partition_list(rd_kafka_event_t *rkev); - - -/** - * @returns a newly allocated topic_partition container, if applicable for the - * event type, else NULL. - * - * @remark The returned pointer MUST be freed with - * rd_kafka_topic_partition_destroy(). - * - * Event types: - * RD_KAFKA_EVENT_ERROR (for partition level errors) - */ -RD_EXPORT rd_kafka_topic_partition_t * -rd_kafka_event_topic_partition(rd_kafka_event_t *rkev); - - -/*! CreateTopics result type */ -typedef rd_kafka_event_t rd_kafka_CreateTopics_result_t; -/*! DeleteTopics result type */ -typedef rd_kafka_event_t rd_kafka_DeleteTopics_result_t; -/*! CreateAcls result type */ -typedef rd_kafka_event_t rd_kafka_CreateAcls_result_t; -/*! DescribeAcls result type */ -typedef rd_kafka_event_t rd_kafka_DescribeAcls_result_t; -/*! DeleteAcls result type */ -typedef rd_kafka_event_t rd_kafka_DeleteAcls_result_t; -/*! CreatePartitions result type */ -typedef rd_kafka_event_t rd_kafka_CreatePartitions_result_t; -/*! AlterConfigs result type */ -typedef rd_kafka_event_t rd_kafka_AlterConfigs_result_t; -/*! CreateTopics result type */ -typedef rd_kafka_event_t rd_kafka_DescribeConfigs_result_t; -/*! DeleteRecords result type */ -typedef rd_kafka_event_t rd_kafka_DeleteRecords_result_t; -/*! DeleteGroups result type */ -typedef rd_kafka_event_t rd_kafka_DeleteGroups_result_t; -/*! DeleteConsumerGroupOffsets result type */ -typedef rd_kafka_event_t rd_kafka_DeleteConsumerGroupOffsets_result_t; - -/** - * @brief Get CreateTopics result. - * - * @returns the result of a CreateTopics request, or NULL if event is of - * different type. - * - * Event types: - * RD_KAFKA_EVENT_CREATETOPICS_RESULT - */ -RD_EXPORT const rd_kafka_CreateTopics_result_t * -rd_kafka_event_CreateTopics_result(rd_kafka_event_t *rkev); - -/** - * @brief Get DeleteTopics result. - * - * @returns the result of a DeleteTopics request, or NULL if event is of - * different type. - * - * Event types: - * RD_KAFKA_EVENT_DELETETOPICS_RESULT - */ -RD_EXPORT const rd_kafka_DeleteTopics_result_t * -rd_kafka_event_DeleteTopics_result(rd_kafka_event_t *rkev); - -/** - * @brief Get CreatePartitions result. - * - * @returns the result of a CreatePartitions request, or NULL if event is of - * different type. - * - * Event types: - * RD_KAFKA_EVENT_CREATEPARTITIONS_RESULT - */ -RD_EXPORT const rd_kafka_CreatePartitions_result_t * -rd_kafka_event_CreatePartitions_result(rd_kafka_event_t *rkev); - -/** - * @brief Get AlterConfigs result. - * - * @returns the result of a AlterConfigs request, or NULL if event is of - * different type. - * - * Event types: - * RD_KAFKA_EVENT_ALTERCONFIGS_RESULT - */ -RD_EXPORT const rd_kafka_AlterConfigs_result_t * -rd_kafka_event_AlterConfigs_result(rd_kafka_event_t *rkev); - -/** - * @brief Get DescribeConfigs result. - * - * @returns the result of a DescribeConfigs request, or NULL if event is of - * different type. - * - * Event types: - * RD_KAFKA_EVENT_DESCRIBECONFIGS_RESULT - */ -RD_EXPORT const rd_kafka_DescribeConfigs_result_t * -rd_kafka_event_DescribeConfigs_result(rd_kafka_event_t *rkev); - -/** - * @returns the result of a DeleteRecords request, or NULL if event is of - * different type. - * - * Event types: - * RD_KAFKA_EVENT_DELETERECORDS_RESULT - */ -RD_EXPORT const rd_kafka_DeleteRecords_result_t * -rd_kafka_event_DeleteRecords_result(rd_kafka_event_t *rkev); - -/** - * @brief Get DeleteGroups result. - * - * @returns the result of a DeleteGroups request, or NULL if event is of - * different type. - * - * Event types: - * RD_KAFKA_EVENT_DELETEGROUPS_RESULT - */ -RD_EXPORT const rd_kafka_DeleteGroups_result_t * -rd_kafka_event_DeleteGroups_result(rd_kafka_event_t *rkev); - -/** - * @brief Get DeleteConsumerGroupOffsets result. - * - * @returns the result of a DeleteConsumerGroupOffsets request, or NULL if - * event is of different type. - * - * Event types: - * RD_KAFKA_EVENT_DELETECONSUMERGROUPOFFSETS_RESULT - */ -RD_EXPORT const rd_kafka_DeleteConsumerGroupOffsets_result_t * -rd_kafka_event_DeleteConsumerGroupOffsets_result(rd_kafka_event_t *rkev); - -/** - * @returns the result of a CreateAcls request, or NULL if event is of - * different type. - * - * Event types: - * RD_KAFKA_EVENT_CREATEACLS_RESULT - */ -RD_EXPORT const rd_kafka_CreateAcls_result_t * -rd_kafka_event_CreateAcls_result(rd_kafka_event_t *rkev); - -/** - * @returns the result of a DescribeAcls request, or NULL if event is of - * different type. - * - * Event types: - * RD_KAFKA_EVENT_DESCRIBEACLS_RESULT - */ -RD_EXPORT const rd_kafka_DescribeAcls_result_t * -rd_kafka_event_DescribeAcls_result(rd_kafka_event_t *rkev); - -/** - * @returns the result of a DeleteAcls request, or NULL if event is of - * different type. - * - * Event types: - * RD_KAFKA_EVENT_DELETEACLS_RESULT - */ -RD_EXPORT const rd_kafka_DeleteAcls_result_t * -rd_kafka_event_DeleteAcls_result(rd_kafka_event_t *rkev); - -/** - * @brief Poll a queue for an event for max \p timeout_ms. - * - * @returns an event, or NULL. - * - * @remark Use rd_kafka_event_destroy() to free the event. - * - * @sa rd_kafka_conf_set_background_event_cb() - */ -RD_EXPORT -rd_kafka_event_t *rd_kafka_queue_poll(rd_kafka_queue_t *rkqu, int timeout_ms); - -/** - * @brief Poll a queue for events served through callbacks for max \p - * timeout_ms. - * - * @returns the number of events served. - * - * @remark This API must only be used for queues with callbacks registered - * for all expected event types. E.g., not a message queue. - * - * @remark Also see rd_kafka_conf_set_background_event_cb() for triggering - * event callbacks from a librdkafka-managed background thread. - * - * @sa rd_kafka_conf_set_background_event_cb() - */ -RD_EXPORT -int rd_kafka_queue_poll_callback(rd_kafka_queue_t *rkqu, int timeout_ms); - - -/**@}*/ - - -/** - * @name Plugin interface - * - * @brief A plugin interface that allows external runtime-loaded libraries - * to integrate with a client instance without modifications to - * the application code. - * - * Plugins are loaded when referenced through the `plugin.library.paths` - * configuration property and operates on the \c rd_kafka_conf_t - * object prior \c rd_kafka_t instance creation. - * - * @warning Plugins require the application to link librdkafka dynamically - * and not statically. Failure to do so will lead to missing symbols - * or finding symbols in another librdkafka library than the - * application was linked with. - */ - - -/** - * @brief Plugin's configuration initializer method called each time the - * library is referenced from configuration (even if previously loaded by - * another client instance). - * - * @remark This method MUST be implemented by plugins and have the symbol name - * \c conf_init - * - * @param conf Configuration set up to this point. - * @param plug_opaquep Plugin can set this pointer to a per-configuration - * opaque pointer. - * @param errstr String buffer of size \p errstr_size where plugin must write - * a human readable error string in the case the initializer - * fails (returns non-zero). - * @param errstr_size Maximum space (including \0) in \p errstr. - * - * @remark A plugin may add an on_conf_destroy() interceptor to clean up - * plugin-specific resources created in the plugin's conf_init() method. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or an error code on error. - */ -typedef rd_kafka_resp_err_t(rd_kafka_plugin_f_conf_init_t)( - rd_kafka_conf_t *conf, - void **plug_opaquep, - char *errstr, - size_t errstr_size); - -/**@}*/ - - - -/** - * @name Interceptors - * - * @{ - * - * @brief A callback interface that allows message interception for both - * producer and consumer data pipelines. - * - * Except for the on_new(), on_conf_set(), on_conf_dup() and on_conf_destroy() - * interceptors, interceptors are added to the - * newly created rd_kafka_t client instance. These interceptors MUST only - * be added from on_new() and MUST NOT be added after rd_kafka_new() returns. - * - * The on_new(), on_conf_set(), on_conf_dup() and on_conf_destroy() interceptors - * are added to the configuration object which is later passed to - * rd_kafka_new() where on_new() is called to allow addition of - * other interceptors. - * - * Each interceptor reference consists of a display name (ic_name), - * a callback function, and an application-specified opaque value that is - * passed as-is to the callback. - * The ic_name must be unique for the interceptor implementation and is used - * to reject duplicate interceptor methods. - * - * Any number of interceptors can be added and they are called in the order - * they were added, unless otherwise noted. - * The list of registered interceptor methods are referred to as - * interceptor chains. - * - * @remark Contrary to the Java client the librdkafka interceptor interface - * does not support message key and value modification. - * Message mutability is discouraged in the Java client and the - * combination of serializers and headers cover most use-cases. - * - * @remark Interceptors are NOT copied to the new configuration on - * rd_kafka_conf_dup() since it would be hard for interceptors to - * track usage of the interceptor's opaque value. - * An interceptor should rely on the plugin, which will be copied - * in rd_kafka_conf_conf_dup(), to set up the initial interceptors. - * An interceptor should implement the on_conf_dup() method - * to manually set up its internal configuration on the newly created - * configuration object that is being copied-to based on the - * interceptor-specific configuration properties. - * conf_dup() should thus be treated the same as conf_init(). - * - * @remark Interceptors are keyed by the interceptor type (on_..()), the - * interceptor name (ic_name) and the interceptor method function. - * Duplicates are not allowed and the .._add_on_..() method will - * return RD_KAFKA_RESP_ERR__CONFLICT if attempting to add a duplicate - * method. - * The only exception is on_conf_destroy() which may be added multiple - * times by the same interceptor to allow proper cleanup of - * interceptor configuration state. - */ - - -/** - * @brief on_conf_set() is called from rd_kafka_*_conf_set() in the order - * the interceptors were added. - * - * @param conf Configuration object. - * @param ic_opaque The interceptor's opaque pointer specified in ..add..(). - * @param name The configuration property to set. - * @param val The configuration value to set, or NULL for reverting to default - * in which case the previous value should be freed. - * @param errstr A human readable error string in case the interceptor fails. - * @param errstr_size Maximum space (including \0) in \p errstr. - * - * @returns RD_KAFKA_CONF_OK if the property was known and successfully - * handled by the interceptor, RD_KAFKA_CONF_INVALID if the - * property was handled by the interceptor but the value was invalid, - * or RD_KAFKA_CONF_UNKNOWN if the interceptor did not handle - * this property, in which case the property is passed on on the - * interceptor in the chain, finally ending up at the built-in - * configuration handler. - */ -typedef rd_kafka_conf_res_t(rd_kafka_interceptor_f_on_conf_set_t)( - rd_kafka_conf_t *conf, - const char *name, - const char *val, - char *errstr, - size_t errstr_size, - void *ic_opaque); - - -/** - * @brief on_conf_dup() is called from rd_kafka_conf_dup() in the - * order the interceptors were added and is used to let - * an interceptor re-register its conf interecptors with a new - * opaque value. - * The on_conf_dup() method is called prior to the configuration from - * \p old_conf being copied to \p new_conf. - * - * @param ic_opaque The interceptor's opaque pointer specified in ..add..(). - * @param new_conf New configuration object. - * @param old_conf Old configuration object to copy properties from. - * @param filter_cnt Number of property names to filter in \p filter. - * @param filter Property names to filter out (ignore) when setting up - * \p new_conf. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or an error code - * on failure (which is logged but otherwise ignored). - * - * @remark No on_conf_* interceptors are copied to the new configuration - * object on rd_kafka_conf_dup(). - */ -typedef rd_kafka_resp_err_t(rd_kafka_interceptor_f_on_conf_dup_t)( - rd_kafka_conf_t *new_conf, - const rd_kafka_conf_t *old_conf, - size_t filter_cnt, - const char **filter, - void *ic_opaque); - - -/** - * @brief on_conf_destroy() is called from rd_kafka_*_conf_destroy() in the - * order the interceptors were added. - * - * @param ic_opaque The interceptor's opaque pointer specified in ..add..(). - */ -typedef rd_kafka_resp_err_t(rd_kafka_interceptor_f_on_conf_destroy_t)( - void *ic_opaque); - - -/** - * @brief on_new() is called from rd_kafka_new() prior toreturning - * the newly created client instance to the application. - * - * @param rk The client instance. - * @param conf The client instance's final configuration. - * @param ic_opaque The interceptor's opaque pointer specified in ..add..(). - * @param errstr A human readable error string in case the interceptor fails. - * @param errstr_size Maximum space (including \0) in \p errstr. - * - * @returns an error code on failure, the error is logged but otherwise ignored. - * - * @warning The \p rk client instance will not be fully set up when this - * interceptor is called and the interceptor MUST NOT call any - * other rk-specific APIs than rd_kafka_interceptor_add..(). - * - */ -typedef rd_kafka_resp_err_t(rd_kafka_interceptor_f_on_new_t)( - rd_kafka_t *rk, - const rd_kafka_conf_t *conf, - void *ic_opaque, - char *errstr, - size_t errstr_size); - - -/** - * @brief on_destroy() is called from rd_kafka_destroy() or (rd_kafka_new() - * if rd_kafka_new() fails during initialization). - * - * @param rk The client instance. - * @param ic_opaque The interceptor's opaque pointer specified in ..add..(). - */ -typedef rd_kafka_resp_err_t( - rd_kafka_interceptor_f_on_destroy_t)(rd_kafka_t *rk, void *ic_opaque); - - - -/** - * @brief on_send() is called from rd_kafka_produce*() (et.al) prior to - * the partitioner being called. - * - * @param rk The client instance. - * @param rkmessage The message being produced. Immutable. - * @param ic_opaque The interceptor's opaque pointer specified in ..add..(). - * - * @remark This interceptor is only used by producer instances. - * - * @remark The \p rkmessage object is NOT mutable and MUST NOT be modified - * by the interceptor. - * - * @remark If the partitioner fails or an unknown partition was specified, - * the on_acknowledgement() interceptor chain will be called from - * within the rd_kafka_produce*() call to maintain send-acknowledgement - * symmetry. - * - * @returns an error code on failure, the error is logged but otherwise ignored. - */ -typedef rd_kafka_resp_err_t(rd_kafka_interceptor_f_on_send_t)( - rd_kafka_t *rk, - rd_kafka_message_t *rkmessage, - void *ic_opaque); - -/** - * @brief on_acknowledgement() is called to inform interceptors that a message - * was succesfully delivered or permanently failed delivery. - * The interceptor chain is called from internal librdkafka background - * threads, or rd_kafka_produce*() if the partitioner failed. - * - * @param rk The client instance. - * @param rkmessage The message being produced. Immutable. - * @param ic_opaque The interceptor's opaque pointer specified in ..add..(). - * - * @remark This interceptor is only used by producer instances. - * - * @remark The \p rkmessage object is NOT mutable and MUST NOT be modified - * by the interceptor. - * - * @warning The on_acknowledgement() method may be called from internal - * librdkafka threads. An on_acknowledgement() interceptor MUST NOT - * call any librdkafka API's associated with the \p rk, or perform - * any blocking or prolonged work. - * - * @returns an error code on failure, the error is logged but otherwise ignored. - */ -typedef rd_kafka_resp_err_t(rd_kafka_interceptor_f_on_acknowledgement_t)( - rd_kafka_t *rk, - rd_kafka_message_t *rkmessage, - void *ic_opaque); - - -/** - * @brief on_consume() is called just prior to passing the message to the - * application in rd_kafka_consumer_poll(), rd_kafka_consume*(), - * the event interface, etc. - * - * @param rk The client instance. - * @param rkmessage The message being consumed. Immutable. - * @param ic_opaque The interceptor's opaque pointer specified in ..add..(). - * - * @remark This interceptor is only used by consumer instances. - * - * @remark The \p rkmessage object is NOT mutable and MUST NOT be modified - * by the interceptor. - * - * @returns an error code on failure, the error is logged but otherwise ignored. - */ -typedef rd_kafka_resp_err_t(rd_kafka_interceptor_f_on_consume_t)( - rd_kafka_t *rk, - rd_kafka_message_t *rkmessage, - void *ic_opaque); - -/** - * @brief on_commit() is called on completed or failed offset commit. - * It is called from internal librdkafka threads. - * - * @param rk The client instance. - * @param offsets List of topic+partition+offset+error that were committed. - * The error message of each partition should be checked for - * error. - * @param err The commit error, if any. - * @param ic_opaque The interceptor's opaque pointer specified in ..add..(). - * - * @remark This interceptor is only used by consumer instances. - * - * @warning The on_commit() interceptor is called from internal - * librdkafka threads. An on_commit() interceptor MUST NOT - * call any librdkafka API's associated with the \p rk, or perform - * any blocking or prolonged work. - * - * - * @returns an error code on failure, the error is logged but otherwise ignored. - */ -typedef rd_kafka_resp_err_t(rd_kafka_interceptor_f_on_commit_t)( - rd_kafka_t *rk, - const rd_kafka_topic_partition_list_t *offsets, - rd_kafka_resp_err_t err, - void *ic_opaque); - - -/** - * @brief on_request_sent() is called when a request has been fully written - * to a broker TCP connections socket. - * - * @param rk The client instance. - * @param sockfd Socket file descriptor. - * @param brokername Broker request is being sent to. - * @param brokerid Broker request is being sent to. - * @param ApiKey Kafka protocol request type. - * @param ApiVersion Kafka protocol request type version. - * @param CorrId Kafka protocol request correlation id. - * @param size Size of request. - * @param ic_opaque The interceptor's opaque pointer specified in ..add..(). - * - * @warning The on_request_sent() interceptor is called from internal - * librdkafka broker threads. An on_request_sent() interceptor MUST NOT - * call any librdkafka API's associated with the \p rk, or perform - * any blocking or prolonged work. - * - * @returns an error code on failure, the error is logged but otherwise ignored. - */ -typedef rd_kafka_resp_err_t(rd_kafka_interceptor_f_on_request_sent_t)( - rd_kafka_t *rk, - int sockfd, - const char *brokername, - int32_t brokerid, - int16_t ApiKey, - int16_t ApiVersion, - int32_t CorrId, - size_t size, - void *ic_opaque); - - -/** - * @brief on_response_received() is called when a protocol response has been - * fully received from a broker TCP connection socket but before the - * response payload is parsed. - * - * @param rk The client instance. - * @param sockfd Socket file descriptor (always -1). - * @param brokername Broker response was received from, possibly empty string - * on error. - * @param brokerid Broker response was received from. - * @param ApiKey Kafka protocol request type or -1 on error. - * @param ApiVersion Kafka protocol request type version or -1 on error. - * @param CorrId Kafka protocol request correlation id, possibly -1 on error. - * @param size Size of response, possibly 0 on error. - * @param rtt Request round-trip-time in microseconds, possibly -1 on error. - * @param err Receive error. - * @param ic_opaque The interceptor's opaque pointer specified in ..add..(). - * - * @warning The on_response_received() interceptor is called from internal - * librdkafka broker threads. An on_response_received() interceptor - * MUST NOT call any librdkafka API's associated with the \p rk, or - * perform any blocking or prolonged work. - * - * @returns an error code on failure, the error is logged but otherwise ignored. - */ -typedef rd_kafka_resp_err_t(rd_kafka_interceptor_f_on_response_received_t)( - rd_kafka_t *rk, - int sockfd, - const char *brokername, - int32_t brokerid, - int16_t ApiKey, - int16_t ApiVersion, - int32_t CorrId, - size_t size, - int64_t rtt, - rd_kafka_resp_err_t err, - void *ic_opaque); - - -/** - * @brief on_thread_start() is called from a newly created librdkafka-managed - * thread. - - * @param rk The client instance. - * @param thread_type Thread type. - * @param thread_name Human-readable thread name, may not be unique. - * @param ic_opaque The interceptor's opaque pointer specified in ..add..(). - * - * @warning The on_thread_start() interceptor is called from internal - * librdkafka threads. An on_thread_start() interceptor MUST NOT - * call any librdkafka API's associated with the \p rk, or perform - * any blocking or prolonged work. - * - * @returns an error code on failure, the error is logged but otherwise ignored. - */ -typedef rd_kafka_resp_err_t(rd_kafka_interceptor_f_on_thread_start_t)( - rd_kafka_t *rk, - rd_kafka_thread_type_t thread_type, - const char *thread_name, - void *ic_opaque); - - -/** - * @brief on_thread_exit() is called just prior to a librdkafka-managed - * thread exiting from the exiting thread itself. - * - * @param rk The client instance. - * @param thread_type Thread type.n - * @param thread_name Human-readable thread name, may not be unique. - * @param ic_opaque The interceptor's opaque pointer specified in ..add..(). - * - * @remark Depending on the thread type, librdkafka may execute additional - * code on the thread after on_thread_exit() returns. - * - * @warning The on_thread_exit() interceptor is called from internal - * librdkafka threads. An on_thread_exit() interceptor MUST NOT - * call any librdkafka API's associated with the \p rk, or perform - * any blocking or prolonged work. - * - * @returns an error code on failure, the error is logged but otherwise ignored. - */ -typedef rd_kafka_resp_err_t(rd_kafka_interceptor_f_on_thread_exit_t)( - rd_kafka_t *rk, - rd_kafka_thread_type_t thread_type, - const char *thread_name, - void *ic_opaque); - - - -/** - * @brief Append an on_conf_set() interceptor. - * - * @param conf Configuration object. - * @param ic_name Interceptor name, used in logging. - * @param on_conf_set Function pointer. - * @param ic_opaque Opaque value that will be passed to the function. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or RD_KAFKA_RESP_ERR__CONFLICT - * if an existing intercepted with the same \p ic_name and function - * has already been added to \p conf. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_conf_interceptor_add_on_conf_set( - rd_kafka_conf_t *conf, - const char *ic_name, - rd_kafka_interceptor_f_on_conf_set_t *on_conf_set, - void *ic_opaque); - - -/** - * @brief Append an on_conf_dup() interceptor. - * - * @param conf Configuration object. - * @param ic_name Interceptor name, used in logging. - * @param on_conf_dup Function pointer. - * @param ic_opaque Opaque value that will be passed to the function. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or RD_KAFKA_RESP_ERR__CONFLICT - * if an existing intercepted with the same \p ic_name and function - * has already been added to \p conf. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_conf_interceptor_add_on_conf_dup( - rd_kafka_conf_t *conf, - const char *ic_name, - rd_kafka_interceptor_f_on_conf_dup_t *on_conf_dup, - void *ic_opaque); - -/** - * @brief Append an on_conf_destroy() interceptor. - * - * @param conf Configuration object. - * @param ic_name Interceptor name, used in logging. - * @param on_conf_destroy Function pointer. - * @param ic_opaque Opaque value that will be passed to the function. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR - * - * @remark Multiple on_conf_destroy() interceptors are allowed to be added - * to the same configuration object. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_conf_interceptor_add_on_conf_destroy( - rd_kafka_conf_t *conf, - const char *ic_name, - rd_kafka_interceptor_f_on_conf_destroy_t *on_conf_destroy, - void *ic_opaque); - - -/** - * @brief Append an on_new() interceptor. - * - * @param conf Configuration object. - * @param ic_name Interceptor name, used in logging. - * @param on_new Function pointer. - * @param ic_opaque Opaque value that will be passed to the function. - * - * @remark Since the on_new() interceptor is added to the configuration object - * it may be copied by rd_kafka_conf_dup(). - * An interceptor implementation must thus be able to handle - * the same interceptor,ic_opaque tuple to be used by multiple - * client instances. - * - * @remark An interceptor plugin should check the return value to make sure it - * has not already been added. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or RD_KAFKA_RESP_ERR__CONFLICT - * if an existing intercepted with the same \p ic_name and function - * has already been added to \p conf. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_conf_interceptor_add_on_new(rd_kafka_conf_t *conf, - const char *ic_name, - rd_kafka_interceptor_f_on_new_t *on_new, - void *ic_opaque); - - - -/** - * @brief Append an on_destroy() interceptor. - * - * @param rk Client instance. - * @param ic_name Interceptor name, used in logging. - * @param on_destroy Function pointer. - * @param ic_opaque Opaque value that will be passed to the function. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or RD_KAFKA_RESP_ERR__CONFLICT - * if an existing intercepted with the same \p ic_name and function - * has already been added to \p conf. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_interceptor_add_on_destroy( - rd_kafka_t *rk, - const char *ic_name, - rd_kafka_interceptor_f_on_destroy_t *on_destroy, - void *ic_opaque); - - -/** - * @brief Append an on_send() interceptor. - * - * @param rk Client instance. - * @param ic_name Interceptor name, used in logging. - * @param on_send Function pointer. - * @param ic_opaque Opaque value that will be passed to the function. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or RD_KAFKA_RESP_ERR__CONFLICT - * if an existing intercepted with the same \p ic_name and function - * has already been added to \p conf. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_interceptor_add_on_send(rd_kafka_t *rk, - const char *ic_name, - rd_kafka_interceptor_f_on_send_t *on_send, - void *ic_opaque); - -/** - * @brief Append an on_acknowledgement() interceptor. - * - * @param rk Client instance. - * @param ic_name Interceptor name, used in logging. - * @param on_acknowledgement Function pointer. - * @param ic_opaque Opaque value that will be passed to the function. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or RD_KAFKA_RESP_ERR__CONFLICT - * if an existing intercepted with the same \p ic_name and function - * has already been added to \p conf. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_interceptor_add_on_acknowledgement( - rd_kafka_t *rk, - const char *ic_name, - rd_kafka_interceptor_f_on_acknowledgement_t *on_acknowledgement, - void *ic_opaque); - - -/** - * @brief Append an on_consume() interceptor. - * - * @param rk Client instance. - * @param ic_name Interceptor name, used in logging. - * @param on_consume Function pointer. - * @param ic_opaque Opaque value that will be passed to the function. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or RD_KAFKA_RESP_ERR__CONFLICT - * if an existing intercepted with the same \p ic_name and function - * has already been added to \p conf. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_interceptor_add_on_consume( - rd_kafka_t *rk, - const char *ic_name, - rd_kafka_interceptor_f_on_consume_t *on_consume, - void *ic_opaque); - - -/** - * @brief Append an on_commit() interceptor. - * - * @param rk Client instance. - * @param ic_name Interceptor name, used in logging. - * @param on_commit() Function pointer. - * @param ic_opaque Opaque value that will be passed to the function. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or RD_KAFKA_RESP_ERR__CONFLICT - * if an existing intercepted with the same \p ic_name and function - * has already been added to \p conf. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_interceptor_add_on_commit( - rd_kafka_t *rk, - const char *ic_name, - rd_kafka_interceptor_f_on_commit_t *on_commit, - void *ic_opaque); - - -/** - * @brief Append an on_request_sent() interceptor. - * - * @param rk Client instance. - * @param ic_name Interceptor name, used in logging. - * @param on_request_sent() Function pointer. - * @param ic_opaque Opaque value that will be passed to the function. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or RD_KAFKA_RESP_ERR__CONFLICT - * if an existing intercepted with the same \p ic_name and function - * has already been added to \p conf. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_interceptor_add_on_request_sent( - rd_kafka_t *rk, - const char *ic_name, - rd_kafka_interceptor_f_on_request_sent_t *on_request_sent, - void *ic_opaque); - - -/** - * @brief Append an on_response_received() interceptor. - * - * @param rk Client instance. - * @param ic_name Interceptor name, used in logging. - * @param on_response_received() Function pointer. - * @param ic_opaque Opaque value that will be passed to the function. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or RD_KAFKA_RESP_ERR__CONFLICT - * if an existing intercepted with the same \p ic_name and function - * has already been added to \p conf. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_interceptor_add_on_response_received( - rd_kafka_t *rk, - const char *ic_name, - rd_kafka_interceptor_f_on_response_received_t *on_response_received, - void *ic_opaque); - - -/** - * @brief Append an on_thread_start() interceptor. - * - * @param rk Client instance. - * @param ic_name Interceptor name, used in logging. - * @param on_thread_start() Function pointer. - * @param ic_opaque Opaque value that will be passed to the function. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or RD_KAFKA_RESP_ERR__CONFLICT - * if an existing intercepted with the same \p ic_name and function - * has already been added to \p conf. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_interceptor_add_on_thread_start( - rd_kafka_t *rk, - const char *ic_name, - rd_kafka_interceptor_f_on_thread_start_t *on_thread_start, - void *ic_opaque); - - -/** - * @brief Append an on_thread_exit() interceptor. - * - * @param rk Client instance. - * @param ic_name Interceptor name, used in logging. - * @param on_thread_exit() Function pointer. - * @param ic_opaque Opaque value that will be passed to the function. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or RD_KAFKA_RESP_ERR__CONFLICT - * if an existing intercepted with the same \p ic_name and function - * has already been added to \p conf. - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_interceptor_add_on_thread_exit( - rd_kafka_t *rk, - const char *ic_name, - rd_kafka_interceptor_f_on_thread_exit_t *on_thread_exit, - void *ic_opaque); - - - -/**@}*/ - - - -/** - * @name Auxiliary types - * - * @{ - */ - - - -/** - * @brief Topic result provides per-topic operation result information. - * - */ - -/** - * @returns the error code for the given topic result. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_topic_result_error(const rd_kafka_topic_result_t *topicres); - -/** - * @returns the human readable error string for the given topic result, - * or NULL if there was no error. - * - * @remark lifetime of the returned string is the same as the \p topicres. - */ -RD_EXPORT const char * -rd_kafka_topic_result_error_string(const rd_kafka_topic_result_t *topicres); - -/** - * @returns the name of the topic for the given topic result. - * @remark lifetime of the returned string is the same as the \p topicres. - * - */ -RD_EXPORT const char * -rd_kafka_topic_result_name(const rd_kafka_topic_result_t *topicres); - -/** - * @brief Group result provides per-group operation result information. - * - */ - -/** - * @returns the error for the given group result, or NULL on success. - * @remark lifetime of the returned error is the same as the \p groupres. - */ -RD_EXPORT const rd_kafka_error_t * -rd_kafka_group_result_error(const rd_kafka_group_result_t *groupres); - -/** - * @returns the name of the group for the given group result. - * @remark lifetime of the returned string is the same as the \p groupres. - * - */ -RD_EXPORT const char * -rd_kafka_group_result_name(const rd_kafka_group_result_t *groupres); - -/** - * @returns the partitions/offsets for the given group result, if applicable - * to the request type, else NULL. - * @remark lifetime of the returned list is the same as the \p groupres. - */ -RD_EXPORT const rd_kafka_topic_partition_list_t * -rd_kafka_group_result_partitions(const rd_kafka_group_result_t *groupres); - - -/**@}*/ - - -/** - * @name Admin API - * @{ - * - * @brief The Admin API enables applications to perform administrative - * Apache Kafka tasks, such as creating and deleting topics, - * altering and reading broker configuration, etc. - * - * The Admin API is asynchronous and makes use of librdkafka's standard - * \c rd_kafka_queue_t queues to propagate the result of an admin operation - * back to the application. - * The supplied queue may be any queue, such as a temporary single-call queue, - * a shared queue used for multiple requests, or even the main queue or - * consumer queues. - * - * Use \c rd_kafka_queue_poll() to collect the result of an admin operation - * from the queue of your choice, then extract the admin API-specific result - * type by using the corresponding \c rd_kafka_event_CreateTopics_result, - * \c rd_kafka_event_DescribeConfigs_result, etc, methods. - * Use the getter methods on the \c .._result_t type to extract response - * information and finally destroy the result and event by calling - * \c rd_kafka_event_destroy(). - * - * Use rd_kafka_event_error() and rd_kafka_event_error_string() to acquire - * the request-level error/success for an Admin API request. - * Even if the returned value is \c RD_KAFKA_RESP_ERR_NO_ERROR there - * may be individual objects (topics, resources, etc) that have failed. - * Extract per-object error information with the corresponding - * \c rd_kafka_..._result_topics|resources|..() to check per-object errors. - * - * Locally triggered errors: - * - \c RD_KAFKA_RESP_ERR__TIMED_OUT - (Controller) broker connection did not - * become available in the time allowed by AdminOption_set_request_timeout. - */ - - -/** - * @enum rd_kafka_admin_op_t - * - * @brief Admin operation enum name for use with rd_kafka_AdminOptions_new() - * - * @sa rd_kafka_AdminOptions_new() - */ -typedef enum rd_kafka_admin_op_t { - RD_KAFKA_ADMIN_OP_ANY = 0, /**< Default value */ - RD_KAFKA_ADMIN_OP_CREATETOPICS, /**< CreateTopics */ - RD_KAFKA_ADMIN_OP_DELETETOPICS, /**< DeleteTopics */ - RD_KAFKA_ADMIN_OP_CREATEPARTITIONS, /**< CreatePartitions */ - RD_KAFKA_ADMIN_OP_ALTERCONFIGS, /**< AlterConfigs */ - RD_KAFKA_ADMIN_OP_DESCRIBECONFIGS, /**< DescribeConfigs */ - RD_KAFKA_ADMIN_OP_DELETERECORDS, /**< DeleteRecords */ - RD_KAFKA_ADMIN_OP_DELETEGROUPS, /**< DeleteGroups */ - /** DeleteConsumerGroupOffsets */ - RD_KAFKA_ADMIN_OP_DELETECONSUMERGROUPOFFSETS, - RD_KAFKA_ADMIN_OP_CREATEACLS, /**< CreateAcls */ - RD_KAFKA_ADMIN_OP_DESCRIBEACLS, /**< DescribeAcls */ - RD_KAFKA_ADMIN_OP_DELETEACLS, /**< DeleteAcls */ - RD_KAFKA_ADMIN_OP__CNT /**< Number of ops defined */ -} rd_kafka_admin_op_t; - -/** - * @brief AdminOptions provides a generic mechanism for setting optional - * parameters for the Admin API requests. - * - * @remark Since AdminOptions is decoupled from the actual request type - * there is no enforcement to prevent setting unrelated properties, - * e.g. setting validate_only on a DescribeConfigs request is allowed - * but is silently ignored by DescribeConfigs. - * Future versions may introduce such enforcement. - */ - - -typedef struct rd_kafka_AdminOptions_s rd_kafka_AdminOptions_t; - -/** - * @brief Create a new AdminOptions object. - * - * The options object is not modified by the Admin API request APIs, - * (e.g. CreateTopics) and may be reused for multiple calls. - * - * @param rk Client instance. - * @param for_api Specifies what Admin API this AdminOptions object will be used - * for, which will enforce what AdminOptions_set_..() calls may - * be used based on the API, causing unsupported set..() calls - * to fail. - * Specifying RD_KAFKA_ADMIN_OP_ANY disables the enforcement - * allowing any option to be set, even if the option - * is not used in a future call to an Admin API method. - * - * @returns a new AdminOptions object (which must be freed with - * rd_kafka_AdminOptions_destroy()), or NULL if \p for_api was set to - * an unknown API op type. - */ -RD_EXPORT rd_kafka_AdminOptions_t * -rd_kafka_AdminOptions_new(rd_kafka_t *rk, rd_kafka_admin_op_t for_api); - - -/** - * @brief Destroy a AdminOptions object. - */ -RD_EXPORT void rd_kafka_AdminOptions_destroy(rd_kafka_AdminOptions_t *options); - - -/** - * @brief Sets the overall request timeout, including broker lookup, - * request transmission, operation time on broker, and response. - * - * @param options Admin options. - * @param timeout_ms Timeout in milliseconds, use -1 for indefinite timeout. - * Defaults to `socket.timeout.ms`. - * @param errstr A human readable error string (nul-terminated) is written to - * this location that must be of at least \p errstr_size bytes. - * The \p errstr is only written in case of error. - * @param errstr_size Writable size in \p errstr. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success, or - * RD_KAFKA_RESP_ERR__INVALID_ARG if timeout was out of range in which - * case an error string will be written \p errstr. - * - * @remark This option is valid for all Admin API requests. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_AdminOptions_set_request_timeout(rd_kafka_AdminOptions_t *options, - int timeout_ms, - char *errstr, - size_t errstr_size); - - -/** - * @brief Sets the broker's operation timeout, such as the timeout for - * CreateTopics to complete the creation of topics on the controller - * before returning a result to the application. - * - * CreateTopics: values <= 0 will return immediately after triggering topic - * creation, while > 0 will wait this long for topic creation to propagate - * in cluster. Default: 60 seconds. - * - * DeleteTopics: same semantics as CreateTopics. - * CreatePartitions: same semantics as CreateTopics. - * - * @param options Admin options. - * @param timeout_ms Timeout in milliseconds. - * @param errstr A human readable error string (nul-terminated) is written to - * this location that must be of at least \p errstr_size bytes. - * The \p errstr is only written in case of error. - * @param errstr_size Writable size in \p errstr. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success, or - * RD_KAFKA_RESP_ERR__INVALID_ARG if timeout was out of range in which - * case an error string will be written \p errstr. - * - * @remark This option is valid for CreateTopics, DeleteTopics, - * CreatePartitions, and DeleteRecords. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_AdminOptions_set_operation_timeout(rd_kafka_AdminOptions_t *options, - int timeout_ms, - char *errstr, - size_t errstr_size); - - -/** - * @brief Tell broker to only validate the request, without performing - * the requested operation (create topics, etc). - * - * @param options Admin options. - * @param true_or_false Defaults to false. - * @param errstr A human readable error string (nul-terminated) is written to - * this location that must be of at least \p errstr_size bytes. - * The \p errstr is only written in case of error. - * @param errstr_size Writable size in \p errstr. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or an - * error code on failure in which case an error string will - * be written \p errstr. - * - * @remark This option is valid for CreateTopics, - * CreatePartitions, AlterConfigs. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_AdminOptions_set_validate_only(rd_kafka_AdminOptions_t *options, - int true_or_false, - char *errstr, - size_t errstr_size); - - -/** - * @brief Override what broker the Admin request will be sent to. - * - * By default, Admin requests are sent to the controller broker, with - * the following exceptions: - * - AlterConfigs with a BROKER resource are sent to the broker id set - * as the resource name. - * - DescribeConfigs with a BROKER resource are sent to the broker id set - * as the resource name. - * - * @param options Admin Options. - * @param broker_id The broker to send the request to. - * @param errstr A human readable error string (nul-terminated) is written to - * this location that must be of at least \p errstr_size bytes. - * The \p errstr is only written in case of error. - * @param errstr_size Writable size in \p errstr. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success or an - * error code on failure in which case an error string will - * be written \p errstr. - * - * @remark This API should typically not be used, but serves as a workaround - * if new resource types are to the broker that the client - * does not know where to send. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_AdminOptions_set_broker(rd_kafka_AdminOptions_t *options, - int32_t broker_id, - char *errstr, - size_t errstr_size); - - - -/** - * @brief Set application opaque value that can be extracted from the - * result event using rd_kafka_event_opaque() - */ -RD_EXPORT void -rd_kafka_AdminOptions_set_opaque(rd_kafka_AdminOptions_t *options, - void *ev_opaque); - - - -/* - * CreateTopics - create topics in cluster. - * - */ - - -/*! Defines a new topic to be created. */ -typedef struct rd_kafka_NewTopic_s rd_kafka_NewTopic_t; - -/** - * @brief Create a new NewTopic object. This object is later passed to - * rd_kafka_CreateTopics(). - * - * @param topic Topic name to create. - * @param num_partitions Number of partitions in topic, or -1 to use the - * broker's default partition count (>= 2.4.0). - * @param replication_factor Default replication factor for the topic's - * partitions, or -1 to use the broker's default - * replication factor (>= 2.4.0) or if - * set_replica_assignment() will be used. - * @param errstr A human readable error string (nul-terminated) is written to - * this location that must be of at least \p errstr_size bytes. - * The \p errstr is only written in case of error. - * @param errstr_size Writable size in \p errstr. - * - * - * @returns a new allocated NewTopic object, or NULL if the input parameters - * are invalid. - * Use rd_kafka_NewTopic_destroy() to free object when done. - */ -RD_EXPORT rd_kafka_NewTopic_t *rd_kafka_NewTopic_new(const char *topic, - int num_partitions, - int replication_factor, - char *errstr, - size_t errstr_size); - -/** - * @brief Destroy and free a NewTopic object previously created with - * rd_kafka_NewTopic_new() - */ -RD_EXPORT void rd_kafka_NewTopic_destroy(rd_kafka_NewTopic_t *new_topic); - - -/** - * @brief Helper function to destroy all NewTopic objects in the \p new_topics - * array (of \p new_topic_cnt elements). - * The array itself is not freed. - */ -RD_EXPORT void rd_kafka_NewTopic_destroy_array(rd_kafka_NewTopic_t **new_topics, - size_t new_topic_cnt); - - -/** - * @brief Set the replica (broker) assignment for \p partition to the - * replica set in \p broker_ids (of \p broker_id_cnt elements). - * - * @remark When this method is used, rd_kafka_NewTopic_new() must have - * been called with a \c replication_factor of -1. - * - * @remark An application must either set the replica assignment for - * all new partitions, or none. - * - * @remark If called, this function must be called consecutively for each - * partition, starting at 0. - * - * @remark Use rd_kafka_metadata() to retrieve the list of brokers - * in the cluster. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success, or an error code - * if the arguments were invalid. - * - * @sa rd_kafka_AdminOptions_set_validate_only() - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_NewTopic_set_replica_assignment(rd_kafka_NewTopic_t *new_topic, - int32_t partition, - int32_t *broker_ids, - size_t broker_id_cnt, - char *errstr, - size_t errstr_size); - -/** - * @brief Set (broker-side) topic configuration name/value pair. - * - * @remark The name and value are not validated by the client, the validation - * takes place on the broker. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success, or an error code - * if the arguments were invalid. - * - * @sa rd_kafka_AdminOptions_set_validate_only() - * @sa http://kafka.apache.org/documentation.html#topicconfigs - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_NewTopic_set_config(rd_kafka_NewTopic_t *new_topic, - const char *name, - const char *value); - - -/** - * @brief Create topics in cluster as specified by the \p new_topics - * array of size \p new_topic_cnt elements. - * - * @param rk Client instance. - * @param new_topics Array of new topics to create. - * @param new_topic_cnt Number of elements in \p new_topics array. - * @param options Optional admin options, or NULL for defaults. - * @param rkqu Queue to emit result on. - * - * Supported admin options: - * - rd_kafka_AdminOptions_set_validate_only() - default false - * - rd_kafka_AdminOptions_set_operation_timeout() - default 60 seconds - * - rd_kafka_AdminOptions_set_request_timeout() - default socket.timeout.ms - * - * @remark The result event type emitted on the supplied queue is of type - * \c RD_KAFKA_EVENT_CREATETOPICS_RESULT - */ -RD_EXPORT void rd_kafka_CreateTopics(rd_kafka_t *rk, - rd_kafka_NewTopic_t **new_topics, - size_t new_topic_cnt, - const rd_kafka_AdminOptions_t *options, - rd_kafka_queue_t *rkqu); - - -/* - * CreateTopics result type and methods - */ - -/** - * @brief Get an array of topic results from a CreateTopics result. - * - * The returned \p topics life-time is the same as the \p result object. - * - * @param result Result to get topics from. - * @param cntp Updated to the number of elements in the array. - */ -RD_EXPORT const rd_kafka_topic_result_t **rd_kafka_CreateTopics_result_topics( - const rd_kafka_CreateTopics_result_t *result, - size_t *cntp); - - - -/* - * DeleteTopics - delete topics from cluster - * - */ - -/*! Represents a topic to be deleted. */ -typedef struct rd_kafka_DeleteTopic_s rd_kafka_DeleteTopic_t; - -/** - * @brief Create a new DeleteTopic object. This object is later passed to - * rd_kafka_DeleteTopics(). - * - * @param topic Topic name to delete. - * - * @returns a new allocated DeleteTopic object. - * Use rd_kafka_DeleteTopic_destroy() to free object when done. - */ -RD_EXPORT rd_kafka_DeleteTopic_t *rd_kafka_DeleteTopic_new(const char *topic); - -/** - * @brief Destroy and free a DeleteTopic object previously created with - * rd_kafka_DeleteTopic_new() - */ -RD_EXPORT void rd_kafka_DeleteTopic_destroy(rd_kafka_DeleteTopic_t *del_topic); - -/** - * @brief Helper function to destroy all DeleteTopic objects in - * the \p del_topics array (of \p del_topic_cnt elements). - * The array itself is not freed. - */ -RD_EXPORT void -rd_kafka_DeleteTopic_destroy_array(rd_kafka_DeleteTopic_t **del_topics, - size_t del_topic_cnt); - -/** - * @brief Delete topics from cluster as specified by the \p topics - * array of size \p topic_cnt elements. - * - * @param rk Client instance. - * @param del_topics Array of topics to delete. - * @param del_topic_cnt Number of elements in \p topics array. - * @param options Optional admin options, or NULL for defaults. - * @param rkqu Queue to emit result on. - * - * @remark The result event type emitted on the supplied queue is of type - * \c RD_KAFKA_EVENT_DELETETOPICS_RESULT - */ -RD_EXPORT -void rd_kafka_DeleteTopics(rd_kafka_t *rk, - rd_kafka_DeleteTopic_t **del_topics, - size_t del_topic_cnt, - const rd_kafka_AdminOptions_t *options, - rd_kafka_queue_t *rkqu); - - - -/* - * DeleteTopics result type and methods - */ - -/** - * @brief Get an array of topic results from a DeleteTopics result. - * - * The returned \p topics life-time is the same as the \p result object. - * - * @param result Result to get topic results from. - * @param cntp is updated to the number of elements in the array. - */ -RD_EXPORT const rd_kafka_topic_result_t **rd_kafka_DeleteTopics_result_topics( - const rd_kafka_DeleteTopics_result_t *result, - size_t *cntp); - - - -/* - * CreatePartitions - add partitions to topic. - * - */ - -/*! Defines a new partition to be created. */ -typedef struct rd_kafka_NewPartitions_s rd_kafka_NewPartitions_t; - -/** - * @brief Create a new NewPartitions. This object is later passed to - * rd_kafka_CreatePartitions() to increase the number of partitions - * to \p new_total_cnt for an existing topic. - * - * @param topic Topic name to create more partitions for. - * @param new_total_cnt Increase the topic's partition count to this value. - * @param errstr A human readable error string (nul-terminated) is written to - * this location that must be of at least \p errstr_size bytes. - * The \p errstr is only written in case of error. - * @param errstr_size Writable size in \p errstr. - * - * @returns a new allocated NewPartitions object, or NULL if the - * input parameters are invalid. - * Use rd_kafka_NewPartitions_destroy() to free object when done. - */ -RD_EXPORT rd_kafka_NewPartitions_t * -rd_kafka_NewPartitions_new(const char *topic, - size_t new_total_cnt, - char *errstr, - size_t errstr_size); - -/** - * @brief Destroy and free a NewPartitions object previously created with - * rd_kafka_NewPartitions_new() - */ -RD_EXPORT void -rd_kafka_NewPartitions_destroy(rd_kafka_NewPartitions_t *new_parts); - -/** - * @brief Helper function to destroy all NewPartitions objects in the - * \p new_parts array (of \p new_parts_cnt elements). - * The array itself is not freed. - */ -RD_EXPORT void -rd_kafka_NewPartitions_destroy_array(rd_kafka_NewPartitions_t **new_parts, - size_t new_parts_cnt); - -/** - * @brief Set the replica (broker id) assignment for \p new_partition_idx to the - * replica set in \p broker_ids (of \p broker_id_cnt elements). - * - * @remark An application must either set the replica assignment for - * all new partitions, or none. - * - * @remark If called, this function must be called consecutively for each - * new partition being created, - * where \p new_partition_idx 0 is the first new partition, - * 1 is the second, and so on. - * - * @remark \p broker_id_cnt should match the topic's replication factor. - * - * @remark Use rd_kafka_metadata() to retrieve the list of brokers - * in the cluster. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR on success, or an error code - * if the arguments were invalid. - * - * @sa rd_kafka_AdminOptions_set_validate_only() - */ -RD_EXPORT rd_kafka_resp_err_t rd_kafka_NewPartitions_set_replica_assignment( - rd_kafka_NewPartitions_t *new_parts, - int32_t new_partition_idx, - int32_t *broker_ids, - size_t broker_id_cnt, - char *errstr, - size_t errstr_size); - - -/** - * @brief Create additional partitions for the given topics, as specified - * by the \p new_parts array of size \p new_parts_cnt elements. - * - * @param rk Client instance. - * @param new_parts Array of topics for which new partitions are to be created. - * @param new_parts_cnt Number of elements in \p new_parts array. - * @param options Optional admin options, or NULL for defaults. - * @param rkqu Queue to emit result on. - * - * Supported admin options: - * - rd_kafka_AdminOptions_set_validate_only() - default false - * - rd_kafka_AdminOptions_set_operation_timeout() - default 60 seconds - * - rd_kafka_AdminOptions_set_request_timeout() - default socket.timeout.ms - * - * @remark The result event type emitted on the supplied queue is of type - * \c RD_KAFKA_EVENT_CREATEPARTITIONS_RESULT - */ -RD_EXPORT void rd_kafka_CreatePartitions(rd_kafka_t *rk, - rd_kafka_NewPartitions_t **new_parts, - size_t new_parts_cnt, - const rd_kafka_AdminOptions_t *options, - rd_kafka_queue_t *rkqu); - - - -/* - * CreatePartitions result type and methods - */ - -/** - * @brief Get an array of topic results from a CreatePartitions result. - * - * The returned \p topics life-time is the same as the \p result object. - * - * @param result Result o get topic results from. - * @param cntp is updated to the number of elements in the array. - */ -RD_EXPORT const rd_kafka_topic_result_t ** -rd_kafka_CreatePartitions_result_topics( - const rd_kafka_CreatePartitions_result_t *result, - size_t *cntp); - - - -/* - * Cluster, broker, topic configuration entries, sources, etc. - * - */ - -/** - * @enum rd_kafka_ConfigSource_t - * - * @brief Apache Kafka config sources. - * - * @remark These entities relate to the cluster, not the local client. - * - * @sa rd_kafka_conf_set(), et.al. for local client configuration. - */ -typedef enum rd_kafka_ConfigSource_t { - /** Source unknown, e.g., in the ConfigEntry used for alter requests - * where source is not set */ - RD_KAFKA_CONFIG_SOURCE_UNKNOWN_CONFIG = 0, - /** Dynamic topic config that is configured for a specific topic */ - RD_KAFKA_CONFIG_SOURCE_DYNAMIC_TOPIC_CONFIG = 1, - /** Dynamic broker config that is configured for a specific broker */ - RD_KAFKA_CONFIG_SOURCE_DYNAMIC_BROKER_CONFIG = 2, - /** Dynamic broker config that is configured as default for all - * brokers in the cluster */ - RD_KAFKA_CONFIG_SOURCE_DYNAMIC_DEFAULT_BROKER_CONFIG = 3, - /** Static broker config provided as broker properties at startup - * (e.g. from server.properties file) */ - RD_KAFKA_CONFIG_SOURCE_STATIC_BROKER_CONFIG = 4, - /** Built-in default configuration for configs that have a - * default value */ - RD_KAFKA_CONFIG_SOURCE_DEFAULT_CONFIG = 5, - - /** Number of source types defined */ - RD_KAFKA_CONFIG_SOURCE__CNT, -} rd_kafka_ConfigSource_t; - - -/** - * @returns a string representation of the \p confsource. - */ -RD_EXPORT const char * -rd_kafka_ConfigSource_name(rd_kafka_ConfigSource_t confsource); - - -/*! Apache Kafka configuration entry. */ -typedef struct rd_kafka_ConfigEntry_s rd_kafka_ConfigEntry_t; - -/** - * @returns the configuration property name - */ -RD_EXPORT const char * -rd_kafka_ConfigEntry_name(const rd_kafka_ConfigEntry_t *entry); - -/** - * @returns the configuration value, may be NULL for sensitive or unset - * properties. - */ -RD_EXPORT const char * -rd_kafka_ConfigEntry_value(const rd_kafka_ConfigEntry_t *entry); - -/** - * @returns the config source. - */ -RD_EXPORT rd_kafka_ConfigSource_t -rd_kafka_ConfigEntry_source(const rd_kafka_ConfigEntry_t *entry); - -/** - * @returns 1 if the config property is read-only on the broker, else 0. - * @remark Shall only be used on a DescribeConfigs result, otherwise returns -1. - */ -RD_EXPORT int -rd_kafka_ConfigEntry_is_read_only(const rd_kafka_ConfigEntry_t *entry); - -/** - * @returns 1 if the config property is set to its default value on the broker, - * else 0. - * @remark Shall only be used on a DescribeConfigs result, otherwise returns -1. - */ -RD_EXPORT int -rd_kafka_ConfigEntry_is_default(const rd_kafka_ConfigEntry_t *entry); - -/** - * @returns 1 if the config property contains sensitive information (such as - * security configuration), else 0. - * @remark An application should take care not to include the value of - * sensitive configuration entries in its output. - * @remark Shall only be used on a DescribeConfigs result, otherwise returns -1. - */ -RD_EXPORT int -rd_kafka_ConfigEntry_is_sensitive(const rd_kafka_ConfigEntry_t *entry); - -/** - * @returns 1 if this entry is a synonym, else 0. - */ -RD_EXPORT int -rd_kafka_ConfigEntry_is_synonym(const rd_kafka_ConfigEntry_t *entry); - - -/** - * @returns the synonym config entry array. - * - * @param entry Entry to get synonyms for. - * @param cntp is updated to the number of elements in the array. - * - * @remark The lifetime of the returned entry is the same as \p conf . - * @remark Shall only be used on a DescribeConfigs result, - * otherwise returns NULL. - */ -RD_EXPORT const rd_kafka_ConfigEntry_t ** -rd_kafka_ConfigEntry_synonyms(const rd_kafka_ConfigEntry_t *entry, - size_t *cntp); - - - -/** - * @enum rd_kafka_ResourceType_t - * @brief Apache Kafka resource types - */ -typedef enum rd_kafka_ResourceType_t { - RD_KAFKA_RESOURCE_UNKNOWN = 0, /**< Unknown */ - RD_KAFKA_RESOURCE_ANY = 1, /**< Any (used for lookups) */ - RD_KAFKA_RESOURCE_TOPIC = 2, /**< Topic */ - RD_KAFKA_RESOURCE_GROUP = 3, /**< Group */ - RD_KAFKA_RESOURCE_BROKER = 4, /**< Broker */ - RD_KAFKA_RESOURCE__CNT, /**< Number of resource types defined */ -} rd_kafka_ResourceType_t; - -/** - * @enum rd_kafka_ResourcePatternType_t - * @brief Apache Kafka pattern types - */ -typedef enum rd_kafka_ResourcePatternType_t { - /** Unknown */ - RD_KAFKA_RESOURCE_PATTERN_UNKNOWN = 0, - /** Any (used for lookups) */ - RD_KAFKA_RESOURCE_PATTERN_ANY = 1, - /** Match: will perform pattern matching */ - RD_KAFKA_RESOURCE_PATTERN_MATCH = 2, - /** Literal: A literal resource name */ - RD_KAFKA_RESOURCE_PATTERN_LITERAL = 3, - /** Prefixed: A prefixed resource name */ - RD_KAFKA_RESOURCE_PATTERN_PREFIXED = 4, - RD_KAFKA_RESOURCE_PATTERN_TYPE__CNT, -} rd_kafka_ResourcePatternType_t; - -/** - * @returns a string representation of the \p resource_pattern_type - */ -RD_EXPORT const char *rd_kafka_ResourcePatternType_name( - rd_kafka_ResourcePatternType_t resource_pattern_type); - -/** - * @returns a string representation of the \p restype - */ -RD_EXPORT const char * -rd_kafka_ResourceType_name(rd_kafka_ResourceType_t restype); - -/*! Apache Kafka configuration resource. */ -typedef struct rd_kafka_ConfigResource_s rd_kafka_ConfigResource_t; - - -/** - * @brief Create new ConfigResource object. - * - * @param restype The resource type (e.g., RD_KAFKA_RESOURCE_TOPIC) - * @param resname The resource name (e.g., the topic name) - * - * @returns a newly allocated object - */ -RD_EXPORT rd_kafka_ConfigResource_t * -rd_kafka_ConfigResource_new(rd_kafka_ResourceType_t restype, - const char *resname); - -/** - * @brief Destroy and free a ConfigResource object previously created with - * rd_kafka_ConfigResource_new() - */ -RD_EXPORT void -rd_kafka_ConfigResource_destroy(rd_kafka_ConfigResource_t *config); - - -/** - * @brief Helper function to destroy all ConfigResource objects in - * the \p configs array (of \p config_cnt elements). - * The array itself is not freed. - */ -RD_EXPORT void -rd_kafka_ConfigResource_destroy_array(rd_kafka_ConfigResource_t **config, - size_t config_cnt); - - -/** - * @brief Set configuration name value pair. - * - * @param config ConfigResource to set config property on. - * @param name Configuration name, depends on resource type. - * @param value Configuration value, depends on resource type and \p name. - * Set to \c NULL to revert configuration value to default. - * - * This will overwrite the current value. - * - * @returns RD_KAFKA_RESP_ERR_NO_ERROR if config was added to resource, - * or RD_KAFKA_RESP_ERR__INVALID_ARG on invalid input. - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_ConfigResource_set_config(rd_kafka_ConfigResource_t *config, - const char *name, - const char *value); - - -/** - * @brief Get an array of config entries from a ConfigResource object. - * - * The returned object life-times are the same as the \p config object. - * - * @param config ConfigResource to get configs from. - * @param cntp is updated to the number of elements in the array. - */ -RD_EXPORT const rd_kafka_ConfigEntry_t ** -rd_kafka_ConfigResource_configs(const rd_kafka_ConfigResource_t *config, - size_t *cntp); - - - -/** - * @returns the ResourceType for \p config - */ -RD_EXPORT rd_kafka_ResourceType_t -rd_kafka_ConfigResource_type(const rd_kafka_ConfigResource_t *config); - -/** - * @returns the name for \p config - */ -RD_EXPORT const char * -rd_kafka_ConfigResource_name(const rd_kafka_ConfigResource_t *config); - -/** - * @returns the error for this resource from an AlterConfigs request - */ -RD_EXPORT rd_kafka_resp_err_t -rd_kafka_ConfigResource_error(const rd_kafka_ConfigResource_t *config); - -/** - * @returns the error string for this resource from an AlterConfigs - * request, or NULL if no error. - */ -RD_EXPORT const char * -rd_kafka_ConfigResource_error_string(const rd_kafka_ConfigResource_t *config); - - -/* - * AlterConfigs - alter cluster configuration. - * - */ - - -/** - * @brief Update the configuration for the specified resources. - * Updates are not transactional so they may succeed for a subset - * of the provided resources while the others fail. - * The configuration for a particular resource is updated atomically, - * replacing values using the provided ConfigEntrys and reverting - * unspecified ConfigEntrys to their default values. - * - * @remark Requires broker version >=0.11.0.0 - * - * @warning AlterConfigs will replace all existing configuration for - * the provided resources with the new configuration given, - * reverting all other configuration to their default values. - * - * @remark Multiple resources and resource types may be set, but at most one - * resource of type \c RD_KAFKA_RESOURCE_BROKER is allowed per call - * since these resource requests must be sent to the broker specified - * in the resource. - * - */ -RD_EXPORT -void rd_kafka_AlterConfigs(rd_kafka_t *rk, - rd_kafka_ConfigResource_t **configs, - size_t config_cnt, - const rd_kafka_AdminOptions_t *options, - rd_kafka_queue_t *rkqu); - - -/* - * AlterConfigs result type and methods - */ - -/** - * @brief Get an array of resource results from a AlterConfigs result. - * - * Use \c rd_kafka_ConfigResource_error() and - * \c rd_kafka_ConfigResource_error_string() to extract per-resource error - * results on the returned array elements. - * - * The returned object life-times are the same as the \p result object. - * - * @param result Result object to get resource results from. - * @param cntp is updated to the number of elements in the array. - * - * @returns an array of ConfigResource elements, or NULL if not available. - */ -RD_EXPORT const rd_kafka_ConfigResource_t ** -rd_kafka_AlterConfigs_result_resources( - const rd_kafka_AlterConfigs_result_t *result, - size_t *cntp); - - - -/* - * DescribeConfigs - retrieve cluster configuration. - * - */ - - -/** - * @brief Get configuration for the specified resources in \p configs. - * - * The returned configuration includes default values and the - * rd_kafka_ConfigEntry_is_default() or rd_kafka_ConfigEntry_source() - * methods may be used to distinguish them from user supplied values. - * - * The value of config entries where rd_kafka_ConfigEntry_is_sensitive() - * is true will always be NULL to avoid disclosing sensitive - * information, such as security settings. - * - * Configuration entries where rd_kafka_ConfigEntry_is_read_only() - * is true can't be updated (with rd_kafka_AlterConfigs()). - * - * Synonym configuration entries are returned if the broker supports - * it (broker version >= 1.1.0). See rd_kafka_ConfigEntry_synonyms(). - * - * @remark Requires broker version >=0.11.0.0 - * - * @remark Multiple resources and resource types may be requested, but at most - * one resource of type \c RD_KAFKA_RESOURCE_BROKER is allowed per call - * since these resource requests must be sent to the broker specified - * in the resource. - */ -RD_EXPORT -void rd_kafka_DescribeConfigs(rd_kafka_t *rk, - rd_kafka_ConfigResource_t **configs, - size_t config_cnt, - const rd_kafka_AdminOptions_t *options, - rd_kafka_queue_t *rkqu); - - - -/* - * DescribeConfigs result type and methods - */ - -/** - * @brief Get an array of resource results from a DescribeConfigs result. - * - * The returned \p resources life-time is the same as the \p result object. - * - * @param result Result object to get resource results from. - * @param cntp is updated to the number of elements in the array. - */ -RD_EXPORT const rd_kafka_ConfigResource_t ** -rd_kafka_DescribeConfigs_result_resources( - const rd_kafka_DescribeConfigs_result_t *result, - size_t *cntp); - - -/* - * DeleteRecords - delete records (messages) from partitions - * - * - */ - -/**! Represents records to be deleted */ -typedef struct rd_kafka_DeleteRecords_s rd_kafka_DeleteRecords_t; - -/** - * @brief Create a new DeleteRecords object. This object is later passed to - * rd_kafka_DeleteRecords(). - * - * \p before_offsets must contain \c topic, \c partition, and - * \c offset is the offset before which the messages will - * be deleted (exclusive). - * Set \c offset to RD_KAFKA_OFFSET_END (high-watermark) in order to - * delete all data in the partition. - * - * @param before_offsets For each partition delete all messages up to but not - * including the specified offset. - * - * @returns a new allocated DeleteRecords object. - * Use rd_kafka_DeleteRecords_destroy() to free object when done. - */ -RD_EXPORT rd_kafka_DeleteRecords_t *rd_kafka_DeleteRecords_new( - const rd_kafka_topic_partition_list_t *before_offsets); - -/** - * @brief Destroy and free a DeleteRecords object previously created with - * rd_kafka_DeleteRecords_new() - */ -RD_EXPORT void -rd_kafka_DeleteRecords_destroy(rd_kafka_DeleteRecords_t *del_records); - -/** - * @brief Helper function to destroy all DeleteRecords objects in - * the \p del_groups array (of \p del_group_cnt elements). - * The array itself is not freed. - */ -RD_EXPORT void -rd_kafka_DeleteRecords_destroy_array(rd_kafka_DeleteRecords_t **del_records, - size_t del_record_cnt); - -/** - * @brief Delete records (messages) in topic partitions older than the - * offsets provided. - * - * @param rk Client instance. - * @param del_records The offsets to delete (up to). - * Currently only one DeleteRecords_t (but containing - * multiple offsets) is supported. - * @param del_record_cnt The number of elements in del_records, must be 1. - * @param options Optional admin options, or NULL for defaults. - * @param rkqu Queue to emit result on. - * - * Supported admin options: - * - rd_kafka_AdminOptions_set_operation_timeout() - default 60 seconds. - * Controls how long the brokers will wait for records to be deleted. - * - rd_kafka_AdminOptions_set_request_timeout() - default socket.timeout.ms. - * Controls how long \c rdkafka will wait for the request to complete. - * - * @remark The result event type emitted on the supplied queue is of type - * \c RD_KAFKA_EVENT_DELETERECORDS_RESULT - */ -RD_EXPORT void rd_kafka_DeleteRecords(rd_kafka_t *rk, - rd_kafka_DeleteRecords_t **del_records, - size_t del_record_cnt, - const rd_kafka_AdminOptions_t *options, - rd_kafka_queue_t *rkqu); - - -/* - * DeleteRecords result type and methods - */ - -/** - * @brief Get a list of topic and partition results from a DeleteRecords result. - * The returned objects will contain \c topic, \c partition, \c offset - * and \c err. \c offset will be set to the post-deletion low-watermark - * (smallest available offset of all live replicas). \c err will be set - * per-partition if deletion failed. - * - * The returned object's life-time is the same as the \p result object. - */ -RD_EXPORT const rd_kafka_topic_partition_list_t * -rd_kafka_DeleteRecords_result_offsets( - const rd_kafka_DeleteRecords_result_t *result); - -/* - * DeleteGroups - delete groups from cluster - * - * - */ - -/*! Represents a group to be deleted. */ -typedef struct rd_kafka_DeleteGroup_s rd_kafka_DeleteGroup_t; - -/** - * @brief Create a new DeleteGroup object. This object is later passed to - * rd_kafka_DeleteGroups(). - * - * @param group Name of group to delete. - * - * @returns a new allocated DeleteGroup object. - * Use rd_kafka_DeleteGroup_destroy() to free object when done. - */ -RD_EXPORT rd_kafka_DeleteGroup_t *rd_kafka_DeleteGroup_new(const char *group); - -/** - * @brief Destroy and free a DeleteGroup object previously created with - * rd_kafka_DeleteGroup_new() - */ -RD_EXPORT void rd_kafka_DeleteGroup_destroy(rd_kafka_DeleteGroup_t *del_group); - -/** - * @brief Helper function to destroy all DeleteGroup objects in - * the \p del_groups array (of \p del_group_cnt elements). - * The array itself is not freed. - */ -RD_EXPORT void -rd_kafka_DeleteGroup_destroy_array(rd_kafka_DeleteGroup_t **del_groups, - size_t del_group_cnt); - -/** - * @brief Delete groups from cluster as specified by the \p del_groups - * array of size \p del_group_cnt elements. - * - * @param rk Client instance. - * @param del_groups Array of groups to delete. - * @param del_group_cnt Number of elements in \p del_groups array. - * @param options Optional admin options, or NULL for defaults. - * @param rkqu Queue to emit result on. - * - * @remark The result event type emitted on the supplied queue is of type - * \c RD_KAFKA_EVENT_DELETEGROUPS_RESULT - */ -RD_EXPORT -void rd_kafka_DeleteGroups(rd_kafka_t *rk, - rd_kafka_DeleteGroup_t **del_groups, - size_t del_group_cnt, - const rd_kafka_AdminOptions_t *options, - rd_kafka_queue_t *rkqu); - - - -/* - * DeleteGroups result type and methods - */ - -/** - * @brief Get an array of group results from a DeleteGroups result. - * - * The returned groups life-time is the same as the \p result object. - * - * @param result Result to get group results from. - * @param cntp is updated to the number of elements in the array. - */ -RD_EXPORT const rd_kafka_group_result_t **rd_kafka_DeleteGroups_result_groups( - const rd_kafka_DeleteGroups_result_t *result, - size_t *cntp); - - -/* - * DeleteConsumerGroupOffsets - delete groups from cluster - * - * - */ - -/*! Represents consumer group committed offsets to be deleted. */ -typedef struct rd_kafka_DeleteConsumerGroupOffsets_s - rd_kafka_DeleteConsumerGroupOffsets_t; - -/** - * @brief Create a new DeleteConsumerGroupOffsets object. - * This object is later passed to rd_kafka_DeleteConsumerGroupOffsets(). - * - * @param group Consumer group id. - * @param partitions Partitions to delete committed offsets for. - * Only the topic and partition fields are used. - * - * @returns a new allocated DeleteConsumerGroupOffsets object. - * Use rd_kafka_DeleteConsumerGroupOffsets_destroy() to free - * object when done. - */ -RD_EXPORT rd_kafka_DeleteConsumerGroupOffsets_t * -rd_kafka_DeleteConsumerGroupOffsets_new( - const char *group, - const rd_kafka_topic_partition_list_t *partitions); - -/** - * @brief Destroy and free a DeleteConsumerGroupOffsets object previously - * created with rd_kafka_DeleteConsumerGroupOffsets_new() - */ -RD_EXPORT void rd_kafka_DeleteConsumerGroupOffsets_destroy( - rd_kafka_DeleteConsumerGroupOffsets_t *del_grpoffsets); - -/** - * @brief Helper function to destroy all DeleteConsumerGroupOffsets objects in - * the \p del_grpoffsets array (of \p del_grpoffsets_cnt elements). - * The array itself is not freed. - */ -RD_EXPORT void rd_kafka_DeleteConsumerGroupOffsets_destroy_array( - rd_kafka_DeleteConsumerGroupOffsets_t **del_grpoffsets, - size_t del_grpoffset_cnt); - -/** - * @brief Delete committed offsets for a set of partitions in a conusmer - * group. This will succeed at the partition level only if the group - * is not actively subscribed to the corresponding topic. - * - * @param rk Client instance. - * @param del_grpoffsets Array of group committed offsets to delete. - * MUST only be one single element. - * @param del_grpoffsets_cnt Number of elements in \p del_grpoffsets array. - * MUST always be 1. - * @param options Optional admin options, or NULL for defaults. - * @param rkqu Queue to emit result on. - * - * @remark The result event type emitted on the supplied queue is of type - * \c RD_KAFKA_EVENT_DELETECONSUMERGROUPOFFSETS_RESULT - * - * @remark The current implementation only supports one group per invocation. - */ -RD_EXPORT -void rd_kafka_DeleteConsumerGroupOffsets( - rd_kafka_t *rk, - rd_kafka_DeleteConsumerGroupOffsets_t **del_grpoffsets, - size_t del_grpoffsets_cnt, - const rd_kafka_AdminOptions_t *options, - rd_kafka_queue_t *rkqu); - - - -/* - * DeleteConsumerGroupOffsets result type and methods - */ - -/** - * @brief Get an array of results from a DeleteConsumerGroupOffsets result. - * - * The returned groups life-time is the same as the \p result object. - * - * @param result Result to get group results from. - * @param cntp is updated to the number of elements in the array. - */ -RD_EXPORT const rd_kafka_group_result_t ** -rd_kafka_DeleteConsumerGroupOffsets_result_groups( - const rd_kafka_DeleteConsumerGroupOffsets_result_t *result, - size_t *cntp); - -/** - * @brief ACL Binding is used to create access control lists. - * - * - */ -typedef struct rd_kafka_AclBinding_s rd_kafka_AclBinding_t; - -/** - * @brief ACL Binding filter is used to filter access control lists. - * - */ -typedef rd_kafka_AclBinding_t rd_kafka_AclBindingFilter_t; - -/** - * @returns the error object for the given acl result, or NULL on success. - */ -RD_EXPORT const rd_kafka_error_t * -rd_kafka_acl_result_error(const rd_kafka_acl_result_t *aclres); - - -/** - * @name AclOperation - * @{ - */ - -/** - * @enum rd_kafka_AclOperation_t - * @brief Apache Kafka ACL operation types. - */ -typedef enum rd_kafka_AclOperation_t { - RD_KAFKA_ACL_OPERATION_UNKNOWN = 0, /**< Unknown */ - RD_KAFKA_ACL_OPERATION_ANY = - 1, /**< In a filter, matches any AclOperation */ - RD_KAFKA_ACL_OPERATION_ALL = 2, /**< ALL operation */ - RD_KAFKA_ACL_OPERATION_READ = 3, /**< READ operation */ - RD_KAFKA_ACL_OPERATION_WRITE = 4, /**< WRITE operation */ - RD_KAFKA_ACL_OPERATION_CREATE = 5, /**< CREATE operation */ - RD_KAFKA_ACL_OPERATION_DELETE = 6, /**< DELETE operation */ - RD_KAFKA_ACL_OPERATION_ALTER = 7, /**< ALTER operation */ - RD_KAFKA_ACL_OPERATION_DESCRIBE = 8, /**< DESCRIBE operation */ - RD_KAFKA_ACL_OPERATION_CLUSTER_ACTION = - 9, /**< CLUSTER_ACTION operation */ - RD_KAFKA_ACL_OPERATION_DESCRIBE_CONFIGS = - 10, /**< DESCRIBE_CONFIGS operation */ - RD_KAFKA_ACL_OPERATION_ALTER_CONFIGS = - 11, /**< ALTER_CONFIGS operation */ - RD_KAFKA_ACL_OPERATION_IDEMPOTENT_WRITE = - 12, /**< IDEMPOTENT_WRITE operation */ - RD_KAFKA_ACL_OPERATION__CNT -} rd_kafka_AclOperation_t; - -/** - * @returns a string representation of the \p acl_operation - */ -RD_EXPORT const char * -rd_kafka_AclOperation_name(rd_kafka_AclOperation_t acl_operation); - -/**@}*/ - -/** - * @name AclPermissionType - * @{ - */ - -/** - * @enum rd_kafka_AclPermissionType_t - * @brief Apache Kafka ACL permission types. - */ -typedef enum rd_kafka_AclPermissionType_t { - RD_KAFKA_ACL_PERMISSION_TYPE_UNKNOWN = 0, /**< Unknown */ - RD_KAFKA_ACL_PERMISSION_TYPE_ANY = - 1, /**< In a filter, matches any AclPermissionType */ - RD_KAFKA_ACL_PERMISSION_TYPE_DENY = 2, /**< Disallows access */ - RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW = 3, /**< Grants access. */ - RD_KAFKA_ACL_PERMISSION_TYPE__CNT -} rd_kafka_AclPermissionType_t; - -/** - * @returns a string representation of the \p acl_permission_type - */ -RD_EXPORT const char *rd_kafka_AclPermissionType_name( - rd_kafka_AclPermissionType_t acl_permission_type); - -/**@}*/ - -/** - * @brief Create a new AclBinding object. This object is later passed to - * rd_kafka_CreateAcls(). - * - * @param restype The ResourceType. - * @param name The resource name. - * @param resource_pattern_type The pattern type. - * @param principal A principal, following the kafka specification. - * @param host An hostname or ip. - * @param operation A Kafka operation. - * @param permission_type A Kafka permission type. - * @param errstr An error string for returning errors or NULL to not use it. - * @param errstr_size The \p errstr size or 0 to not use it. - * - * @returns a new allocated AclBinding object, or NULL if the input parameters - * are invalid. - * Use rd_kafka_AclBinding_destroy() to free object when done. - */ -RD_EXPORT rd_kafka_AclBinding_t * -rd_kafka_AclBinding_new(rd_kafka_ResourceType_t restype, - const char *name, - rd_kafka_ResourcePatternType_t resource_pattern_type, - const char *principal, - const char *host, - rd_kafka_AclOperation_t operation, - rd_kafka_AclPermissionType_t permission_type, - char *errstr, - size_t errstr_size); - -/** - * @brief Create a new AclBindingFilter object. This object is later passed to - * rd_kafka_DescribeAcls() or - * rd_kafka_DeletesAcls() in order to filter - * the acls to retrieve or to delete. - * Use the same rd_kafka_AclBinding functions to query or destroy it. - * - * @param restype The ResourceType or \c RD_KAFKA_RESOURCE_ANY if - * not filtering by this field. - * @param name The resource name or NULL if not filtering by this field. - * @param resource_pattern_type The pattern type or \c - * RD_KAFKA_RESOURCE_PATTERN_ANY if not filtering by this field. - * @param principal A principal or NULL if not filtering by this field. - * @param host An hostname or ip or NULL if not filtering by this field. - * @param operation A Kafka operation or \c RD_KAFKA_ACL_OPERATION_ANY if not - * filtering by this field. - * @param permission_type A Kafka permission type or \c - * RD_KAFKA_ACL_PERMISSION_TYPE_ANY if not filtering by this field. - * @param errstr An error string for returning errors or NULL to not use it. - * @param errstr_size The \p errstr size or 0 to not use it. - * - * @returns a new allocated AclBindingFilter object, or NULL if the input - * parameters are invalid. Use rd_kafka_AclBinding_destroy() to free object when - * done. - */ -RD_EXPORT rd_kafka_AclBindingFilter_t *rd_kafka_AclBindingFilter_new( - rd_kafka_ResourceType_t restype, - const char *name, - rd_kafka_ResourcePatternType_t resource_pattern_type, - const char *principal, - const char *host, - rd_kafka_AclOperation_t operation, - rd_kafka_AclPermissionType_t permission_type, - char *errstr, - size_t errstr_size); - -/** - * @returns the resource type for the given acl binding. - */ -RD_EXPORT rd_kafka_ResourceType_t -rd_kafka_AclBinding_restype(const rd_kafka_AclBinding_t *acl); - -/** - * @returns the resource name for the given acl binding. - * - * @remark lifetime of the returned string is the same as the \p acl. - */ -RD_EXPORT const char * -rd_kafka_AclBinding_name(const rd_kafka_AclBinding_t *acl); - -/** - * @returns the principal for the given acl binding. - * - * @remark lifetime of the returned string is the same as the \p acl. - */ -RD_EXPORT const char * -rd_kafka_AclBinding_principal(const rd_kafka_AclBinding_t *acl); - -/** - * @returns the host for the given acl binding. - * - * @remark lifetime of the returned string is the same as the \p acl. - */ -RD_EXPORT const char * -rd_kafka_AclBinding_host(const rd_kafka_AclBinding_t *acl); - -/** - * @returns the acl operation for the given acl binding. - */ -RD_EXPORT rd_kafka_AclOperation_t -rd_kafka_AclBinding_operation(const rd_kafka_AclBinding_t *acl); - -/** - * @returns the permission type for the given acl binding. - */ -RD_EXPORT rd_kafka_AclPermissionType_t -rd_kafka_AclBinding_permission_type(const rd_kafka_AclBinding_t *acl); - -/** - * @returns the resource pattern type for the given acl binding. - */ -RD_EXPORT rd_kafka_ResourcePatternType_t -rd_kafka_AclBinding_resource_pattern_type(const rd_kafka_AclBinding_t *acl); - -/** - * @returns the error object for the given acl binding, or NULL on success. - */ -RD_EXPORT const rd_kafka_error_t * -rd_kafka_AclBinding_error(const rd_kafka_AclBinding_t *acl); - - -/** - * @brief Destroy and free an AclBinding object previously created with - * rd_kafka_AclBinding_new() - */ -RD_EXPORT void rd_kafka_AclBinding_destroy(rd_kafka_AclBinding_t *acl_binding); - - -/** - * @brief Helper function to destroy all AclBinding objects in - * the \p acl_bindings array (of \p acl_bindings_cnt elements). - * The array itself is not freed. - */ -RD_EXPORT void -rd_kafka_AclBinding_destroy_array(rd_kafka_AclBinding_t **acl_bindings, - size_t acl_bindings_cnt); - -/** - * @brief Get an array of acl results from a CreateAcls result. - * - * The returned \p acl result life-time is the same as the \p result object. - * @param result CreateAcls result to get acl results from. - * @param cntp is updated to the number of elements in the array. - */ -RD_EXPORT const rd_kafka_acl_result_t ** -rd_kafka_CreateAcls_result_acls(const rd_kafka_CreateAcls_result_t *result, - size_t *cntp); - -/** - * @brief Create acls as specified by the \p new_acls - * array of size \p new_topic_cnt elements. - * - * @param rk Client instance. - * @param new_acls Array of new acls to create. - * @param new_acls_cnt Number of elements in \p new_acls array. - * @param options Optional admin options, or NULL for defaults. - * @param rkqu Queue to emit result on. - * - * Supported admin options: - * - rd_kafka_AdminOptions_set_request_timeout() - default socket.timeout.ms - * - * @remark The result event type emitted on the supplied queue is of type - * \c RD_KAFKA_EVENT_CREATEACLS_RESULT - */ -RD_EXPORT void rd_kafka_CreateAcls(rd_kafka_t *rk, - rd_kafka_AclBinding_t **new_acls, - size_t new_acls_cnt, - const rd_kafka_AdminOptions_t *options, - rd_kafka_queue_t *rkqu); - -/** - * @section DescribeAcls - describe access control lists. - * - * - */ - -/** - * @brief Get an array of resource results from a DescribeAcls result. - * - * The returned \p resources life-time is the same as the \p result object. - * @param result DescribeAcls result to get acls from. - * @param cntp is updated to the number of elements in the array. - */ -RD_EXPORT const rd_kafka_AclBinding_t ** -rd_kafka_DescribeAcls_result_acls(const rd_kafka_DescribeAcls_result_t *result, - size_t *cntp); - -/** - * @brief Describe acls matching the filter provided in \p acl_filter - * - * @param rk Client instance. - * @param acl_filter Filter for the returned acls. - * @param options Optional admin options, or NULL for defaults. - * @param rkqu Queue to emit result on. - * - * Supported admin options: - * - rd_kafka_AdminOptions_set_operation_timeout() - default 0 - * - * @remark The result event type emitted on the supplied queue is of type - * \c RD_KAFKA_EVENT_DESCRIBEACLS_RESULT - */ -RD_EXPORT void rd_kafka_DescribeAcls(rd_kafka_t *rk, - rd_kafka_AclBindingFilter_t *acl_filter, - const rd_kafka_AdminOptions_t *options, - rd_kafka_queue_t *rkqu); - -/** - * @section DeleteAcls - delete access control lists. - * - * - */ - -typedef struct rd_kafka_DeleteAcls_result_response_s - rd_kafka_DeleteAcls_result_response_t; - -/** - * @brief Get an array of DeleteAcls result responses from a DeleteAcls result. - * - * The returned \p responses life-time is the same as the \p result object. - * @param result DeleteAcls result to get responses from. - * @param cntp is updated to the number of elements in the array. - */ -RD_EXPORT const rd_kafka_DeleteAcls_result_response_t ** -rd_kafka_DeleteAcls_result_responses(const rd_kafka_DeleteAcls_result_t *result, - size_t *cntp); - -/** - * @returns the error object for the given DeleteAcls result response, - * or NULL on success. - */ -RD_EXPORT const rd_kafka_error_t *rd_kafka_DeleteAcls_result_response_error( - const rd_kafka_DeleteAcls_result_response_t *result_response); - - -/** - * @returns the matching acls array for the given DeleteAcls result response. - * - * @remark lifetime of the returned acl bindings is the same as the \p - * result_response. - */ -RD_EXPORT const rd_kafka_AclBinding_t ** -rd_kafka_DeleteAcls_result_response_matching_acls( - const rd_kafka_DeleteAcls_result_response_t *result_response, - size_t *matching_acls_cntp); - -/** - * @brief Delete acls matching the filteres provided in \p del_acls - * array of size \p del_acls_cnt. - * - * @param rk Client instance. - * @param del_acls Filters for the acls to delete. - * @param del_acls_cnt Number of elements in \p del_acls array. - * @param options Optional admin options, or NULL for defaults. - * @param rkqu Queue to emit result on. - * - * Supported admin options: - * - rd_kafka_AdminOptions_set_operation_timeout() - default 0 - * - * @remark The result event type emitted on the supplied queue is of type - * \c RD_KAFKA_EVENT_DELETEACLS_RESULT - */ -RD_EXPORT void rd_kafka_DeleteAcls(rd_kafka_t *rk, - rd_kafka_AclBindingFilter_t **del_acls, - size_t del_acls_cnt, - const rd_kafka_AdminOptions_t *options, - rd_kafka_queue_t *rkqu); - -/**@}*/ - -/** - * @name Security APIs - * @{ - * - */ - -/** - * @brief Set SASL/OAUTHBEARER token and metadata - * - * @param rk Client instance. - * @param token_value the mandatory token value to set, often (but not - * necessarily) a JWS compact serialization as per - * https://tools.ietf.org/html/rfc7515#section-3.1. - * @param md_lifetime_ms when the token expires, in terms of the number of - * milliseconds since the epoch. - * @param md_principal_name the mandatory Kafka principal name associated - * with the token. - * @param extensions optional SASL extensions key-value array with - * \p extensions_size elements (number of keys * 2), where [i] is the key and - * [i+1] is the key's value, to be communicated to the broker - * as additional key-value pairs during the initial client response as per - * https://tools.ietf.org/html/rfc7628#section-3.1. The key-value pairs are - * copied. - * @param extension_size the number of SASL extension keys plus values, - * which must be a non-negative multiple of 2. - * @param errstr A human readable error string (nul-terminated) is written to - * this location that must be of at least \p errstr_size bytes. - * The \p errstr is only written in case of error. - * @param errstr_size Writable size in \p errstr. - * - * The SASL/OAUTHBEARER token refresh callback or event handler should invoke - * this method upon success. The extension keys must not include the reserved - * key "`auth`", and all extension keys and values must conform to the required - * format as per https://tools.ietf.org/html/rfc7628#section-3.1: - * - * key = 1*(ALPHA) - * value = *(VCHAR / SP / HTAB / CR / LF ) - * - * @returns \c RD_KAFKA_RESP_ERR_NO_ERROR on success, otherwise \p errstr set - * and:
- * \c RD_KAFKA_RESP_ERR__INVALID_ARG if any of the arguments are - * invalid;
- * \c RD_KAFKA_RESP_ERR__NOT_IMPLEMENTED if SASL/OAUTHBEARER is not - * supported by this build;
- * \c RD_KAFKA_RESP_ERR__STATE if SASL/OAUTHBEARER is supported but is - * not configured as the client's authentication mechanism.
- * - * @sa rd_kafka_oauthbearer_set_token_failure - * @sa rd_kafka_conf_set_oauthbearer_token_refresh_cb - */ -RD_EXPORT -rd_kafka_resp_err_t -rd_kafka_oauthbearer_set_token(rd_kafka_t *rk, - const char *token_value, - int64_t md_lifetime_ms, - const char *md_principal_name, - const char **extensions, - size_t extension_size, - char *errstr, - size_t errstr_size); - -/** - * @brief SASL/OAUTHBEARER token refresh failure indicator. - * - * @param rk Client instance. - * @param errstr mandatory human readable error reason for failing to acquire - * a token. - * - * The SASL/OAUTHBEARER token refresh callback or event handler should invoke - * this method upon failure. - * - * @returns \c RD_KAFKA_RESP_ERR_NO_ERROR on success, otherwise:
- * \c RD_KAFKA_RESP_ERR__NOT_IMPLEMENTED if SASL/OAUTHBEARER is not - * supported by this build;
- * \c RD_KAFKA_RESP_ERR__STATE if SASL/OAUTHBEARER is supported but is - * not configured as the client's authentication mechanism,
- * \c RD_KAFKA_RESP_ERR__INVALID_ARG if no error string is supplied. - * - * @sa rd_kafka_oauthbearer_set_token - * @sa rd_kafka_conf_set_oauthbearer_token_refresh_cb - */ -RD_EXPORT -rd_kafka_resp_err_t rd_kafka_oauthbearer_set_token_failure(rd_kafka_t *rk, - const char *errstr); - -/**@}*/ - - -/** - * @name Transactional producer API - * - * The transactional producer operates on top of the idempotent producer, - * and provides full exactly-once semantics (EOS) for Apache Kafka when used - * with the transaction aware consumer (\c isolation.level=read_committed). - * - * A producer instance is configured for transactions by setting the - * \c transactional.id to an identifier unique for the application. This - * id will be used to fence stale transactions from previous instances of - * the application, typically following an outage or crash. - * - * After creating the transactional producer instance using rd_kafka_new() - * the transactional state must be initialized by calling - * rd_kafka_init_transactions(). This is a blocking call that will - * acquire a runtime producer id from the transaction coordinator broker - * as well as abort any stale transactions and fence any still running producer - * instances with the same \c transactional.id. - * - * Once transactions are initialized the application may begin a new - * transaction by calling rd_kafka_begin_transaction(). - * A producer instance may only have one single on-going transaction. - * - * Any messages produced after the transaction has been started will - * belong to the ongoing transaction and will be committed or aborted - * atomically. - * It is not permitted to produce messages outside a transaction - * boundary, e.g., before rd_kafka_begin_transaction() or after - * rd_kafka_commit_transaction(), rd_kafka_abort_transaction(), or after - * the current transaction has failed. - * - * If consumed messages are used as input to the transaction, the consumer - * instance must be configured with \c enable.auto.commit set to \c false. - * To commit the consumed offsets along with the transaction pass the - * list of consumed partitions and the last offset processed + 1 to - * rd_kafka_send_offsets_to_transaction() prior to committing the transaction. - * This allows an aborted transaction to be restarted using the previously - * committed offsets. - * - * To commit the produced messages, and any consumed offsets, to the - * current transaction, call rd_kafka_commit_transaction(). - * This call will block until the transaction has been fully committed or - * failed (typically due to fencing by a newer producer instance). - * - * Alternatively, if processing fails, or an abortable transaction error is - * raised, the transaction needs to be aborted by calling - * rd_kafka_abort_transaction() which marks any produced messages and - * offset commits as aborted. - * - * After the current transaction has been committed or aborted a new - * transaction may be started by calling rd_kafka_begin_transaction() again. - * - * @par Retriable errors - * Some error cases allow the attempted operation to be retried, this is - * indicated by the error object having the retriable flag set which can - * be detected by calling rd_kafka_error_is_retriable(). - * When this flag is set the application may retry the operation immediately - * or preferably after a shorter grace period (to avoid busy-looping). - * Retriable errors include timeouts, broker transport failures, etc. - * - * @par Abortable errors - * An ongoing transaction may fail permanently due to various errors, - * such as transaction coordinator becoming unavailable, write failures to the - * Apache Kafka log, under-replicated partitions, etc. - * At this point the producer application must abort the current transaction - * using rd_kafka_abort_transaction() and optionally start a new transaction - * by calling rd_kafka_begin_transaction(). - * Whether an error is abortable or not is detected by calling - * rd_kafka_error_txn_requires_abort() on the returned error object. - * - * @par Fatal errors - * While the underlying idempotent producer will typically only raise - * fatal errors for unrecoverable cluster errors where the idempotency - * guarantees can't be maintained, most of these are treated as abortable by - * the transactional producer since transactions may be aborted and retried - * in their entirety; - * The transactional producer on the other hand introduces a set of additional - * fatal errors which the application needs to handle by shutting down the - * producer and terminate. There is no way for a producer instance to recover - * from fatal errors. - * Whether an error is fatal or not is detected by calling - * rd_kafka_error_is_fatal() on the returned error object or by checking - * the global rd_kafka_fatal_error() code. - * Fatal errors are raised by triggering the \c error_cb (see the - * Fatal error chapter in INTRODUCTION.md for more information), and any - * sub-sequent transactional API calls will return RD_KAFKA_RESP_ERR__FATAL - * or have the fatal flag set (see rd_kafka_error_is_fatal()). - * The originating fatal error code can be retrieved by calling - * rd_kafka_fatal_error(). - * - * @par Handling of other errors - * For errors that have neither retriable, abortable or the fatal flag set - * it is not always obvious how to handle them. While some of these errors - * may be indicative of bugs in the application code, such as when - * an invalid parameter is passed to a method, other errors might originate - * from the broker and be passed thru as-is to the application. - * The general recommendation is to treat these errors, that have - * neither the retriable or abortable flags set, as fatal. - * - * @par Error handling example - * @code - * retry: - * rd_kafka_error_t *error; - * - * error = rd_kafka_commit_transaction(producer, 10*1000); - * if (!error) - * return success; - * else if (rd_kafka_error_txn_requires_abort(error)) { - * do_abort_transaction_and_reset_inputs(); - * } else if (rd_kafka_error_is_retriable(error)) { - * rd_kafka_error_destroy(error); - * goto retry; - * } else { // treat all other errors as fatal errors - * fatal_error(rd_kafka_error_string(error)); - * } - * rd_kafka_error_destroy(error); - * @endcode - * - * - * @{ - */ - - -/** - * @brief Initialize transactions for the producer instance. - * - * This function ensures any transactions initiated by previous instances - * of the producer with the same \c transactional.id are completed. - * If the previous instance failed with a transaction in progress the - * previous transaction will be aborted. - * This function needs to be called before any other transactional or - * produce functions are called when the \c transactional.id is configured. - * - * If the last transaction had begun completion (following transaction commit) - * but not yet finished, this function will await the previous transaction's - * completion. - * - * When any previous transactions have been fenced this function - * will acquire the internal producer id and epoch, used in all future - * transactional messages issued by this producer instance. - * - * @param rk Producer instance. - * @param timeout_ms The maximum time to block. On timeout the operation - * may continue in the background, depending on state, - * and it is okay to call init_transactions() again. - * - * @remark This function may block up to \p timeout_ms milliseconds. - * - * @returns NULL on success or an error object on failure. - * Check whether the returned error object permits retrying - * by calling rd_kafka_error_is_retriable(), or whether a fatal - * error has been raised by calling rd_kafka_error_is_fatal(). - * Error codes: - * RD_KAFKA_RESP_ERR__TIMED_OUT if the transaction coordinator - * could be not be contacted within \p timeout_ms (retriable), - * RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE if the transaction - * coordinator is not available (retriable), - * RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS if a previous transaction - * would not complete within \p timeout_ms (retriable), - * RD_KAFKA_RESP_ERR__STATE if transactions have already been started - * or upon fatal error, - * RD_KAFKA_RESP_ERR__UNSUPPORTED_FEATURE if the broker(s) do not - * support transactions (