From bad90afc6f5be37fae11358c949802503e77caef Mon Sep 17 00:00:00 2001 From: Jonathan Ellis Date: Tue, 27 Dec 2011 20:17:17 +0000 Subject: [PATCH] add row-level isolation via SnapTree patch by slebresne; reviewed by jbellis for CASSANDRA-2893 git-svn-id: https://svn.apache.org/repos/asf/cassandra/trunk@1225001 13f79535-47bb-0310-9956-ffa450edef68 --- lib/licenses/snaptree-0.1-SNAPSHOT.txt | 776 ++++++++++++++++++ lib/snaptree-0.1-SNAPSHOT.jar | Bin 0 -> 113450 bytes .../cassandra/db/AbstractColumnContainer.java | 99 +-- .../db/AbstractThreadUnsafeSortedColumns.java | 111 +++ .../db/ArrayBackedSortedColumns.java | 95 ++- .../cassandra/db/AtomicSortedColumns.java | 394 +++++++++ .../cassandra/db/CollationController.java | 4 +- .../org/apache/cassandra/db/ColumnFamily.java | 8 +- .../cassandra/db/ColumnFamilySerializer.java | 2 +- .../apache/cassandra/db/ISortedColumns.java | 27 +- .../org/apache/cassandra/db/Memtable.java | 37 +- src/java/org/apache/cassandra/db/Row.java | 2 +- .../org/apache/cassandra/db/RowMutation.java | 2 +- .../org/apache/cassandra/db/SuperColumn.java | 13 +- .../cassandra/db/ThreadSafeSortedColumns.java | 175 ---- .../db/TreeMapBackedSortedColumns.java | 61 +- .../db/ArrayBackedSortedColumnsTest.java | 4 +- .../apache/cassandra/db/ColumnFamilyTest.java | 2 +- 18 files changed, 1445 insertions(+), 367 deletions(-) create mode 100644 lib/licenses/snaptree-0.1-SNAPSHOT.txt create mode 100644 lib/snaptree-0.1-SNAPSHOT.jar create mode 100644 src/java/org/apache/cassandra/db/AbstractThreadUnsafeSortedColumns.java create mode 100644 src/java/org/apache/cassandra/db/AtomicSortedColumns.java delete mode 100644 src/java/org/apache/cassandra/db/ThreadSafeSortedColumns.java diff --git a/lib/licenses/snaptree-0.1-SNAPSHOT.txt b/lib/licenses/snaptree-0.1-SNAPSHOT.txt new file mode 100644 index 0000000000..07324c5323 --- /dev/null +++ b/lib/licenses/snaptree-0.1-SNAPSHOT.txt @@ -0,0 +1,776 @@ + + + + + + + + + doc/LICENSE at master from nbronson/snaptree - GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+

+ nbronson / + snaptree +

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +

Latest commit to the master branch

+ +
+

+ update to new IDEA install + +

+
+ commit b198f84b0c + +
+ + nbronson + authored + +
+
+
+ + +
+ + + +
+
+ + +
+
+
+
+ Txt + 100644 + 36 lines (27 sloc) + 1.715 kb +
+ +
+
+ + + + + +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+
+
+
SNAPTREE LICENSE

Copyright (c) 2009 Stanford University, unless otherwise specified.
All rights reserved.

This software was developed by the Pervasive Parallelism Laboratory of
Stanford University, California, USA.

Permission to use, copy, modify, and distribute this software in source
or binary form for any purpose with or without fee is hereby granted,
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.

   3. Neither the name of Stanford University nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.


THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.

+
+
+ +
+
+
+
+ +
+ + + +
+
+ + + + + + + + +
+

Markdown Cheat Sheet

+ +
+ +
+
+

Format Text

+

Headers

+
+# This is an <h1> tag
+## This is an <h2> tag
+###### This is an <h6> tag
+

Text styles

+
+*This text will be italic*
+_This will also be italic_
+**This text will be bold**
+__This will also be bold__
+
+*You **can** combine them*
+
+
+
+

Lists

+

Unordered

+
+* Item 1
+* Item 2
+  * Item 2a
+  * Item 2b
+

Ordered

+
+1. Item 1
+2. Item 2
+3. Item 3
+   * Item 3a
+   * Item 3b
+
+
+

Miscellaneous

+

Images

+
+![GitHub Logo](/images/logo.png)
+Format: ![Alt Text](url)
+
+

Links

+
+http://github.com - automatic!
+[GitHub](http://github.com)
+

Blockquotes

+
+As Kanye West said:
+
+> We're living the future so
+> the present is our past.
+
+
+
+
+ +

Code Examples in Markdown

+
+

Syntax highlighting with GFM

+
+```javascript
+function fancyAlert(arg) {
+  if(arg) {
+    $.facebox({div:'#foo'})
+  }
+}
+```
+
+
+

Or, indent your code 4 spaces

+
+Here is a Python code example
+without syntax highlighting:
+
+    def foo:
+      if not bar:
+        return true
+
+
+

Inline code for comments

+
+I think you should use an
+`<addr>` element here instead.
+
+
+ +
+ + + +
+ +
+

Something went wrong with that request. Please try again. Dismiss

+
+ + + + + + + diff --git a/lib/snaptree-0.1-SNAPSHOT.jar b/lib/snaptree-0.1-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..6aa73424a1b0ccf5981f5dfc054ffd978e07fa30 GIT binary patch literal 113450 zcmb5U1C%CByCqs(w#_cvwr$(CjV{~umTgyc*|u%lc6DL;oBy9P=ght9p1HXqGIC|^ zhz#r}Ru9$wyY>66lo=8mu#wPR9qP3BxWTvACw8Dhrdsxn4P71_uIzWB0P(S5c5%W zYXLU#Z)RTLcVcFpI-NQ||Ep#|K!dS%|D^$-e`{uH;`$$T|9>Lj{*7>UF|;$YcQX0^ zfFk{$PzMK_{{$cUKi-Y|{{}ay@SbNT4fhXDez!2<$f|8H_C z+Zj4Y7&=?X8aj{&IT_j+|8qrWY-8x`yrk~!preZQo!t@Fv1T)Gn^>Zo0jhEuXi0YW)4L zCP>8_Cat>oabr>PW4D$lxe0G_DDH22BU|?lEfO!`F0~Rb5k+BtOkb<2cz@2Hn9R-x z)j!8eAVfWSVs-xNpoy@%3`I&&lvsvw`l@d<$o$jC1?#PIG;plqg8{?o|PzwGn!> zaTpuLRd!-$=ASaNWuVN|nGuu#n;B8#KW#Of#)hXR)Dff$<*yko#}zU};$$eyz%3?X zdo=4gr}CoC!dUCPuiy2V7%xWGQ=%y4m+!AgWa?Xgf|lA$$j>(DYbT&X=6MC4Xz1~r zQ1s0nQOdht4e!TNU9+V%?28Z7+6%|>U)4f5!C)isW(e2vaDGU;Itp@x2uxD9Ch;jP z?INn`>R1T>fCb}~87}ljn}o}9IikyCI(jCb_hX?1W!kcT2s|pFFm?~_+8kuIiE-6A zKMXvwj&HP`t{NXKYBIRQ)k!5Pmt zxJhr;?{?n5=ra59;FhIQ1_w%Cy8M=N_`~!1ZSiNL3#BVTdkWLd`90A@85RFLF`&S(x zZbXD#&>Z$VG`Aqj4mvG5Rf`<6FNoO)YLHi|zZqmh@lN6MXD%FwvNnarxQYC*Gop{~ zACQL%TAa5CdgAnHTm<$QpcKiilQq%bfHrIpe4!sqt@A{*N$;=}0&|GB>F)(s;)VC) zNqADtepjd_*W5QExfOFZQA0@!SsPr765=vBIs8<2(qiM8@Y(^U-r)d~hrs4d{I-mJ zPu<=y<-M*Mx4GMy1_EzFaHC!#H80zXi~-aZHWbhpv**1?(w;GW+q1{*fD&HWl~E~Q z$fL8RzzcmtF=UwvK=D(mu)NWY6Z3;D6l%h6<6diOi7WQfluds|x$st%0A4*tFaaPNL%!z-$NmNM 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%0Av9ywp`!^WkrOZLRY8tCsC8eEemzIhBod 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%0DgP>p~D<*F+pJ1otP$gtOnBKAi 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

y@8l#U#ZGQP9%VSMq29K6RGFXb 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 z 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

xZqi4}w1~c|hwNFxMb@PjyWk zZ;(Pu=m$$7uV1;5JCq8e#;1azDpt>H{mT7Gt_(=@;}HO>$U$dnlNqAy?t@gpgFMp? zi4y>Otf2{ZKrQQq>j=_e0@I(!xnrHSr#^J^x&D2wr`oSQ_SZ`|b4_*q+a<|1{GPim z4YF&@u|M{niFgT0uv!^WFV9xLDt*U0ZaXc6GDiQ)F5JV&gA#&#J4nZF;UCa{`APqA z7@hgj>tTPa4kSDf5YvBSb^ag93IF9Q)~fHhpsJyLw{Dm(u{M`dg)OcY2}+5`#6SsC zwE!xbQ;V8ab(A*9WttMnol-FFvM2&B_hXlxT;~h<3qX{$#RiffEk0#jM=b~d;-9lK z>{J4yOcx?Id@p$}w{No^vmH4G-=8-Sfn)dC1TCD$W72SGO4#WK^>8H2ybMFI%(0V< ze>cc+uZ%Cm$#|F=d_p4 zxrS!pTJU%LY)7BDSyp5>RUnI!Mah0b$3c@Vwj~2U>VSl^f(1S1grBaCFSwcWQK}&e|}sn7$81FFpX=du&MY zY}Fnpp9!J7<8M<_=Y1E|#4y`q86_f%d7T92Hbaz?kI`9Z5~ui<6`30kyV`1X<#@B( zvyimBT*#MWKJA4|o(9Bn<|h>q&*i^sM1)2yT5iiT7ULC4$|RwmMr|O-5EU71D6Yol z6-XlwA}rMRjDMu7nhFYGH;$pYI}3*}n3$1pCFY1E9jfH2R_-coFZgh3yDc_1MWkr) zAS?BtNXeCjoUei`B`S>}4b(hBb6kA}^Cw@R)7MmGl-uw~`-4)il<=XCbSdkBNMC5Pe2JMJlN% zte-ZOkJ7+TbB5Ui?Rn7!0U^LO!g@gPmT>#XN!fRrK$rFWf#yl<RNjkkr7h{`^2g>gHdQoL$Z1;G zL6dlk<5B)f^tlqR=+;91mMYrysyVRylFYOQJeu^=xVX&R$qEp_5Vrq-`roB9k+fqd zY;_m4VDCrCPRbX_(dFrqlP8-F^Y3N<2fq)T^tx(wDTp-4dNg+D-)V~kcLlL7HlFxo z2XQVx|E@m;5nF#0vFnyj_u<#lX9u9xuw3rKE`41nyJUS3H#ym>n|_Oo;iT@0g6|3= zIiQ~Mh`!M~^++h(nxNJqP|ej^QczP;VU|mx7)2$MiYFwKO6y6X7*r;c3X4dgan}{PKMbLYmE`;RpV7K-BiCIZBt(Y`DB@A@ae?)kw@SSCLOip_KDZA z;N861*-ImK#BYJsbkDzOyF1%guxm3vxLw||UpeRaU0@8s##N1#Y9MiP##?2tUv~Mk z)FcDJXW{oB-Lsv~21z9Z zD@zFCwu9PIy8J`ju-7zxQm3-i#W_aarjE|u@Dn{{)391*`)lRxnhJ+Vx&rNg-GcG@ zd-Z|)3-+am?2EepN($0WMtJqAwMhEi)W=`0Ite8A;FG;jCGJe}U_hLQ5R4Ny#Ru)o z#rTVNgY=cV6c$tp26ctSkaqv-Un5MiVQJj2Rj9qqkJX$M-5e_&tm4>bN4RUHsTYQn zWw<6{e&{{-02Hr2GTVe+@^p!sUPCH6!qHh6AGOZ%92D>Ob3tlS{Z@w{cWVEHc&g9L zHOTfqKH&6E8i4*Y_&+U+Ay6D_#g9cf{u%$BCeQy+IU!ZGGO_tzkxW=D&rb>h5PG`C zD;v9y4i>ZCzK(XYbGEv=KUUZpNn(K~BK<0l%Lxg@UI!n!tCRz%yP7^W20Vr$J*Kc!NJ zuZ!Ak5QSp0Zr@TvT?h5>Eo*6If9N=&pvN@CDPq z1OGqgEKV&jED8k#6#S#^!t&o(Qx#WdD@QX^6)SHu)BiF5{~5yNYJ6y*onra-_ApK{ zQeuiFsbv62=-B|5=uqg$@My*8#RIKOM>qAc^ej0WTEH`h2dZI8inIjlYNuy)1b z$IC7kT<1R<*B8#a#ADlIbITl07d;_v!r2AK-nHM}so(BYEWy89h5+z-P!6|WLtc0- z-Lf(vu=&Jd`PgD=xYU6wvGC92`R985h7W#}^Ful2UG9B_&`(Eis zZCSlM*5#X>A!AN9>xcG}+@?Aku2Rh+h0gP{oMi@gv|$+pY6_=;Y4pt}eoI*HJ+3WHi)R1a`N8Ud z*Cw`4v!9@hr=CO|*B-`m&YWQ5^KadQRUoXlM6_?8d0m3FsYingsF&%?BZw3_iaTmf zFym-yWa*BhqZWSG4>pW`ncQTa;NkHz{h=1ses5(qBIdK#4u!BCGiJ_h=|=stQA5_c zotnjG<=t8%lQT(_Ex*<9RaX?A;`>U%o@=c`&IhAbG{)W}wyva~5*fX^1Vfb5Uk&NU z;`SYxMU$1FdDJ>rP3IS6#x_biqw-#m_9Qo3o0`QiQ~ZZX(|EjvzbR6 zagdI_5&$ZXfQY!Vuw;fB?n^={=o>KA50bVov~nSiK5lxZaA;3?%3-j zQ&PHYcPia>hM4X+XCi$;`3I*Ey8{J5sGhU^Qoq>msBB5nURcTw^g#RieIWEht@bm5 zt%gFfmhNak`$iq$WTV1*)-4Qq@)qxO79M#(`^L6la89Wv%xmGA<}92tS}0jrYIHtB zEwau-6SK#?S})0bIh+`RhSvlb>1qOdK6{>VHhWZtnu7!m6(GK2U*L5^%nI;g&s)t_ z!hE_649CxYHWdjl)v8F6Y zE?kuJ*rbm}yiEcsSeLBuap?`x`Np2E529{t)s5yzp8qKaF!Ra0=UeWzi3agb%O~C| zN5TFLE1B18P!4r<=&4VS>G~^Aqmeckf6@nvwn`h++ht znegCdcK5I$^<`b>Et_3YT1gZLM)cnNs}eR5uybb3o?l=SV2DAzi3w#0%-4o5<^aU9Z(x&5;mF>wy15o9}7k|$d{Pm)L zN>EI*g_$d$=EXzy7<`~@H6&|w&O2VSreRN<=8B1G905as#K?y(S%Hv$gP_d%#+1AC z#gC2A=yWs05B-A$@F{{{pwVa$D`>Wf+mwjpuok&)K*VXmQso%^W)G`+mgdKyxz#N` zxQ>;Bd~!=%iD%xXP{i`x9RnyTFc%Rw#N1yI;Ro%FXxf#rfmrk>%20;3yByHOtrGB<{st` zMsYzZb?GXW+!OP_H#}X**b8jwr!IBaDS;~CLHfm_@GT*B9)d@BetgsHu1Hg=t}lcf zWcF{_BD1;3;|6K-?MefFlZ>vBVRqd8O{NM8Q#< zg}zB-iT{?|6U!W+EWM@$Mv^wz5*Av7Y#R*tR2HO{{KlYSl9cG&X0}EuT{9YKwu$r& zwx$YZouKn}Z%y@t2We`G60CM0;a6?@#$eyJ$`;X)sNVnAQS6y(xz|svo0pLYf1>)E z?CFJ8L$d1k;U#jGa}RzY|65b7y@{SN#!Qo^Ekus_{VTlaMMDREi<`o<@f7^BquLsC zOfAQ}C!OU3h=~}Xew_Drwp51kbgBH+zyFuqT=v$GpTiFTS_TFPh~vL=N`7iW|Em-t zX7B3!e;}Y`R8WPGzR7>k*&>2@!ITx04ud*m$Z^PEQJQ3`HG@mZHSMa6Qg-Yx9DHXR zT><^Yx+Rk#0{sML8!TN~B9Y6Z)GlY&=QroS%lo9}<_z`%fHFn6;o{AR4vj)>i87#x zZk)DHJ^PbP6;V11b!gyHimm80qSEMAJk?lT8kDIBrZD1cM_Ad0qU~_3t;Uv{%dM&S z(~Y1?qPgM<>1uH{)l2Kkvv@A_^JwwTjn!u?R8=r-#sKyDmuBJVYD3Hk?6fnck+y(v z^RmCz9W_ZdyEO0hIV7|FFmNV$m*!_&dfQ5s+J@a;ufwzEz7(hih0RU+9&1-t8>=ty zD^;1Hz55+6_8`BLwH{qUb>txjspk6lgjjS7sihtYB+AB)$05ocxAUoF+jc@6vL5$NL4 zL==-J=Xtoz9jZ0G`TsP^t-!NCFW+sb#U&=rJtH*Npr3jU1BGMCF`hgT&rb5}^ZY_K zPB$uw%7r8Nx3y#S4&)*{4ckCCT!bXUN|@|pHl^B>&d0z{!JaY4&bidzde{-H9onfw z=7r(DLY*Du(t&vu(H4WwQeAs)oY}l)C>xWD9J;_T90sM{fIq+}&dF}+$jawtsrfl| zZb_fdhcSj4J_{jHTzHWs8lLz{V7K7X-yrp`itdeZI@UXHYRDM!$#K|(SLCuX`~2D( z0$St|Ec{GiLs+2{B)=fcG*rFo@d^6!br||{tmJJ$nwhrdlP>rZNlE)=3A<=(!E#+v*|_u=oBJ!CeYkU=_LU&3sZ;W8*6M>XiyQ_sOk#uJ(&k8ALz< zX)1e8L%t&JqVV}D@&@R#B;r`#hLmgo7KBj>ui^_&)3}Dbtt2*F`3_w9F5KBp+}RG? z*)CjqNrbRg$(8%;iz?gq+|}Jtytkg1nB;pxvEPdKM%%+8+sfMG=Kq9E4TwLm{Rl`t zasDf?_)j@=UvtO0fP z3)^E0PKzZIW=Im^xZE?-q5?GCIhvX(hx32ps@m>jp=z`ub*=YQ>Hrn$lQotmOhXzi zX_!uh{H3ql53Fy$r!fN{`9j+Q)HPRn7KqNf(jcwrN;7>rSQG=i;Nc=|d&=2l?R5BU zQF*1d<-aZ3_A=Z5S7p}$PUZK<31u|QC?k7jk1{hdb8WKAxc1&7{kn+UGK;LNu31J> zB#|hjP>Q0+NJhwrQt5x*a=9+9{>Sq^@9TN)`F`H>UFUnw`JVR($2H0leT#0ar0geg z=&ROOk0#yS_V&hWm&dn}izlX>?wSkO&gELyj#vA=XceF-o-KFooihpyjpe+SGXx{q zy{F=rlMomAeRI=0M0?vMm4maZ?^xe&?b1M`vw7!&@q|gq^)?pKJWVP!OO;iAY#WpuIuh2G4&aSl`c za5+&w?3~D*#u}@_aTuTYol~7ouRxJ-T^?KP6GD+)0qhwZmj31!q3gfTRDQmMWuE;t)SL&fzaK8jjS!c)(4cE>gTE6K*^T?cDTyUN@i z)k~ddYTUUK=8|*TnoOCB>U4cHW5JlgU1LUH(-gm!M#pp-I$4JwLHp z<{!_hdw&0Osl|W|OX=Ibo6l>V8T6bxtY!<%bNzcbAJto==pu6`t(-E$nM|>;{Z}$&9Yo$vh{v|F*zcK4pj;VkbT-G1CZQqQgigIV~ofD8{k3uS2@)XNS^msPeYFME`GsZthGqrM^ihV zM?Ob3rIDoe8`&&j-M+wV>7=OnSxNI1#oN?kilJbN(z{X3a{Iui`c+;Xw7l_6zD?oj zcM08HLUdWAEtLlYFQ)v*oXN}-lJuQ^)}Nhlgg}GVScf+_zLB600j!_z# z8K6~LXgnqVBqN7yiF(OH`wV@a*U^zQe@dz6W~jg;S*>uNr)i^!l6%X{=n^W0S*HhU zYCfgO(a=$)mK2DIh~2rIz@kSnKe#(WUHL$=n2OqCkBrCh95jp9CKp<5gncC9qV*)6 zbb2;^J*lRiPI~s#AYJ!p1u1_d-Kip--_5i*4{#pk>K`z! z7_g7qNfu!~6Ea37G+vAH*qf;nTi@GIE4as5Kl@I7v38*IVfiyrRAOI3vkrZD?y14) z)Y3rZbis4_KkwOyO3x}Qrz4Ev<~AB#DG7V>Y%lZhe6!0ZXs&+y>npX-YyH#VJPxmn z{u_Omd@50q%bPOu{7gCvs`}LoS3+f?r1dWM-!=JcOk%IynreF{3-a~PTk!_Qj}@IC z2s%nJU*Q!*ueu;%CPW^pHsRakb7o4qODAWSMuN_%JLED=CwXH{rEU|v2zo`B(ly?E zF#dkX@jX{1Nm*f16&>*oqffqM6>2T}_n(MC+1QIQ(vtsVZ>b|2I=1FX zp#=}sn{(#|L1IW1SoDzmhiFI-ZU^^*yV<}Myu7ReF_L6*6Wg_^6sVi(IxUQi$v#H9 z;uo^c+L-kS)DbCADuz1h37aYndKyQRSGj;wF$mk2St2gd3NnQsr;26zd9o&8fw}fT z8Q}}1fim}z=bwY8=W3He1T|2`rm{3Q;5UP~-QB#-)~?QLDU9QSw<6RO#igx90U?2AU#jtdz- z_a9Uh4Qo1`5*2%4xHjihTKCY>2bO9PPg#G$$>_GeYd^nbJqm8_i%{@OR-D#dqR@~* zg1P6AD>Y(4j{Zr0_Z|JHJV(n3DMtxjY32xtPvpP9khgmzR$DgvGQJrlTftk!vy(zP)w(2}E8j4b~e^WM8T6VfU z)T{q8>yZNzZHgzShx+E)?k&niFaKPu?8kqWUBVKXC}wb`V1U#}{GbJm9V|Hq>C9W= zJoKDxKPyTqfht#m!Y5#)-t+3Li+OGlS1Qm^O)t{@h${v-uq*J2| zp1BLNchwxUZ#H7M>R#dRUsdNAf25%hX>S(I-|F8eV`{rsmIF!3bd;SLOuvUj+$-U} zK$o3W|32u;xdhfLM+Xa3%oudp-!XDA2N>Bk8xXWEFD1RcvilLM(B}+e-$euEq$^48 zqU@6qpC}&(vi(Q-cuZ>FBes0B^l(tNs>!#azVP0v1{zgaHyP$!EHYHOoy`H6v=V`3s{4|HC zZtZ!y@4W`K?vCQ|J0WSOya_KNY_voN0~nR6t`AY~G&Jya8Khz~tGZ#cBXj)C6Iq%} z;b(qnmJ#PKi-&)hJ1-OwBRG?|_e9)VMDg{VB@K<#4OcYHr9EQKnIbAD-kdl5UKHSF zVePLjr@3gGFX9ZQK5Mdi+$3Z(HE3zMKOY>wWZv|nZ19)3Hi)&!W9?|R5P>M84My5eyE+vFVD1G{&h)_x<-BB((ZOP_2TPR z$e1+FFLST@MTJIX!^n=pnE2FQ1mm&Map?IlKM~s79MAk@*df#}czD!R(TLcU!CqK> zbXu)PR*-}}chG|7Q*uI9)_)y>{+HA`V%fPB+#AySRA+TAmy2-bX?M8Cz+xxaIIeJ` zhD&28cW_E;2lc7$Btu!p8z26_AfQgfIbb)oJ6oQDtC94QF+w4Qj*CdR_b?@kS$RUS znv6yHg-J>tDo>E=>sBW}Eg>!6BXe$njDJDRlxNJtM6W~X)3*xBcArg1!{b-ui>)?(`_cr$QZ;FBV9e$E^_$8%Uy-mpW}a z602f{4=UN)j2+iyTiwXa1&&e z-=0pT$FzPaZn0JFgO5t|yD!$2`qls^IKRxCJK$ZoYwZjyaA=&i$1%{)ua{KxcGR%;j3hb z@e-m8j_GI*w8eoW-lt&VM(F<`HW%Nyb!!=bky*MR@I!=FQ*Hnp?Aw; z((+HKzkLimPs^?JHo%9bF3ULM=BQ!Q5C!9>?6XO5{hzf#=D}e|Sa?N@`rhasz9b3M zAsTxv?(ea8L%IhRuL`hpMn2~3wH>vc6aKmrr6+mC{q)Ug^)UYWFmrtaI>U*`bVbYS zcB922k}s>-tL{4%C3{-8R?TH6YDyExdnC9x{XCj^le|1D*p65(^7%)WyU#j&%Fk8@ zoAe*6qdcRwGyZ01qf9t#`TBmiLq`R-^R$CwBb7~0=Xm9ej+7k#%>o7&8u6Bc{Hi6q z{2D@+jpn^ya8KA}2A18f=e>1~?;ee^>!Y;JVdW^pI|K6hqp2SPv`QI8iK-T)GTKYN zoivM;R(PR|PhBsokPx;rC2V*HYoqu0jm2^DNoSsSM7OS-yjeBe*_m>kUhF}Z(&3W_ z3kW7&PfG5UW8jKvXgqss;dkzJjJ{MU*6mn6eWQpskg5;3peZ>lecYg8t4ab%*Tc)* z1McPH2>0I8QcwCwXg6L*a2;#e?Od8jkv9sq1(7hT9|#B01D7gLCiQjQ{vodulL-@d z)YNv^ut*plIuK#}@X*6UX-luF4hp;{4F2?hhdnUroz#T?kjL4W$|hSm%1WPo{O6zB zDQ8?KOnd31khNdyJkvOr+4QpoWp6`;q}cCA$-#?t}@ zc|=@U&;k75EV!tlU#=7sf(tyYR)Z;MsjDdK8wkKu)+pfNHG()ET?4vO+N)yM0NG9$HHKDdpv@X*RSI-XJP2$ruu%wXwd!Awu!ancIaX_c2Zq&upz5-l zRkv|>v+?x;gAN~z7a_)jC?)&+d~k{k56^}1ui~JIp%DJtP=d1k)vUZ7U{)UNie6T3 zHqg`Gm~4$ve?h+AO|Au-`tb_Ebrg*N?kEHw;M<1QV!SRUh$`F-?q%iU=TN*BI! zPYxJ}6@d(WOb($C7xw-;%1JLrAGm>om%FdM!=DN3&B3_I7HM-q6Z*gfd1Z7#AtHf1 zaW(00YQoC408CE(*=1m`{QWj)!yRxiDHIc<3ku;)iJL6MDfA?rzOOYTl}#`pk%x*q zzc~jm(pCnnYZyT^+hd?bzH*wQm5=+%iH?{}fna3S3_4DO&c++0FRT~3F5UJpnEOcn zYS%_NKn!bhc(tb)sMRBY1f?6I3ktCd7{<18fYJ`xl^h&hY&VjJ8ZoasEL{S~UjpP0 zqiKUX3c9N_9O+PZ|05*~s_+eAt&y1V$TGr&ocZOI}$Z&p~1iY^V zaGy}#Kf0h0&DyvrW9pES_DPui7NWgu^XjZR;7TLVqV<->roTM`)&vMHv!2oNIA#yK zS|0**=m>D@vgo#eI||WafE!H7-6K%j?TZRT!x;*fNQ$6ZVk>oJ$1O z2W8u!3ks2Kfg1|q9Hz={VtSBh&XpxK90E*>TG>Rj1|Vp)JpkIgMK;PE(pCCb_ZOxC zadp5fX*6YUM(xcNtu6agkzwp<&_%unz4m%1HDreyXiat5S8+Br2IADq z!OU77ur?tN`X!Ndjk)TK8v|2`*RqPSMNR%)I7Ahwa4BFII4X0spb+E%+tbGsH7@;y z#B!}Yu!|g|Z;^2PJuVjwg+km4+#U%n7HGm_%l?)5R^2yNAdpUA@#_Ubjr@0rKN4A8 z+QDX+Ypz|gE?`zO=rz{6b7ai+7?=#(%CkUXR}2i=RR}mV4laH)r@$SB5RS)<^2ej(mZyQregGZMdcW(KxIMsnueiBU z5H;DKsRpjY4GcNu*KvnFXL|&+`&fw%{)*ezdP|A>{%T>H1k6eWXj1FYL<_b@Lo2R= zwYQI#m5q=3>e0{GnsrTjQeX*~A`j4DC|E}q6e6G$H_jHW9O^ZQFD~od1(b*Y;q@j_ zc?&nuO?r(jH0pW4$J=p0qwWHctjDM?$BhAL)OvxlZ=qCGbA=PLfH6maVnK&(qYDa= z3%2Frif^!e?Ur(;eb_N54tl*s(Ce+&+wp4L5KuGFa>m>>h%I5Z6L}w60Hd4$0yH{B z7Zk#?_TLd8q(4f#(KsN9vwy5FZ~+dGmlnGjvGuq?HcEVRge(#1G-uF^3Lua51_5jR ze-YRo~| zn!>*^3;^YB06Oz|dmuEsjI3OI;gB2LJhFl$KdC!X-w!<2YoKB4J6YrD?Saq^9vXD0 zxx3iHHwoV%6#Z-DU)_M@pMl}tdOePw`FE7<9edJ}XZSBLz^`G(9-Ha^*d7NXWY}DZ z74EU=exSg^D*%DQ`dA^81Rtks#PD02leR1vdSVR(&H+3-wABDzP>84WxJhGp-A%L> z;=ULo*WOVO558r^!{b8}26xciG2y1Y8scpJ(u-XhN)zCvwKw?jLP6YwG5Ep$M@zjo zB0#Us!eN)A1&sOvP@eUj(TK?Q2x#?OnK{@T0{iTGG=sprio`!dY$Of2B<#^E#AE#aoj9(J8hi8R8UkSmZUhXy z-+Iu6y@O|syk!_}@_$r+-F_ZS&O_40-YK(2TQ3|p?Jf4rU;;p*#m;(N1Nd}l8vu-{ zxow8M*lBrdAUD9`4X$txv+Kt_lEY3|S;P36xIG3|*Sxi`v6uJPU<6XOhru+rwZ;B5 znQUAv0Hc(Z0?mVuU&CLU^+zwttxck1uQV{D>;9R8r_I<<8L%FEk#1{hJnN_#{sT2{ zpdvnh7wynwH($0xQ$``u{~xj|i+F4NBgWfo*=UV=P}cS=SbgdBi%2>rh(Mf#ho=Jm PULL^1Q_Kbt4c`9%QC@;o literal 0 HcmV?d00001 diff --git a/src/java/org/apache/cassandra/db/AbstractColumnContainer.java b/src/java/org/apache/cassandra/db/AbstractColumnContainer.java index d1fbcb058c..959dcefbf3 100644 --- a/src/java/org/apache/cassandra/db/AbstractColumnContainer.java +++ b/src/java/org/apache/cassandra/db/AbstractColumnContainer.java @@ -18,31 +18,24 @@ package org.apache.cassandra.db; import java.nio.ByteBuffer; -import java.security.MessageDigest; import java.util.Collection; import java.util.Iterator; -import java.util.Map; import java.util.SortedSet; -import java.util.concurrent.ConcurrentSkipListMap; -import java.util.concurrent.atomic.AtomicReference; +import com.google.common.base.Function; +import com.google.common.base.Functions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.apache.cassandra.config.CFMetaData; -import org.apache.cassandra.config.DatabaseDescriptor; -import org.apache.cassandra.db.filter.QueryPath; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.io.util.IIterableColumns; import org.apache.cassandra.utils.Allocator; -import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.HeapAllocator; public abstract class AbstractColumnContainer implements IColumnContainer, IIterableColumns { private static Logger logger = LoggerFactory.getLogger(AbstractColumnContainer.class); - protected final AtomicReference deletionInfo = new AtomicReference(new DeletionInfo()); protected final ISortedColumns columns; protected AbstractColumnContainer(ISortedColumns columns) @@ -53,20 +46,12 @@ public abstract class AbstractColumnContainer implements IColumnContainer, IIter @Deprecated // TODO this is a hack to set initial value outside constructor public void delete(int localtime, long timestamp) { - deletionInfo.set(new DeletionInfo(timestamp, localtime)); + columns.delete(new ISortedColumns.DeletionInfo(timestamp, localtime)); } public void delete(AbstractColumnContainer cc2) { - // Keeping deletion info for max markedForDeleteAt value - DeletionInfo current; - DeletionInfo cc2Info = cc2.deletionInfo.get(); - while (true) - { - current = deletionInfo.get(); - if (current.markedForDeleteAt >= cc2Info.markedForDeleteAt || deletionInfo.compareAndSet(current, cc2Info)) - break; - } + columns.delete(cc2.columns.getDeletionInfo()); } public boolean isMarkedForDelete() @@ -76,12 +61,12 @@ public abstract class AbstractColumnContainer implements IColumnContainer, IIter public long getMarkedForDeleteAt() { - return deletionInfo.get().markedForDeleteAt; + return columns.getDeletionInfo().markedForDeleteAt; } public int getLocalDeletionTime() { - return deletionInfo.get().localDeletionTime; + return columns.getDeletionInfo().localDeletionTime; } public AbstractType getComparator() @@ -96,27 +81,20 @@ public abstract class AbstractColumnContainer implements IColumnContainer, IIter */ public void maybeResetDeletionTimes(int gcBefore) { - while (true) - { - DeletionInfo current = deletionInfo.get(); - // Stop if either we don't need to change the deletion info (it's - // still MIN_VALUE or not expired yet) or we've succesfully changed it - if (current.localDeletionTime == Integer.MIN_VALUE - || current.localDeletionTime > gcBefore - || deletionInfo.compareAndSet(current, new DeletionInfo())) - { - break; - } - } + columns.maybeResetDeletionTimes(gcBefore); } /** * We need to go through each column in the column container and resolve it before adding */ + public void addAll(AbstractColumnContainer cc, Allocator allocator, Function transformation) + { + columns.addAll(cc.columns, allocator, transformation); + } + public void addAll(AbstractColumnContainer cc, Allocator allocator) { - columns.addAll(cc.columns, allocator); - delete(cc); + addAll(cc, allocator, Functions.identity()); } public void addColumn(IColumn column) @@ -166,39 +144,7 @@ public abstract class AbstractColumnContainer implements IColumnContainer, IIter public void retainAll(AbstractColumnContainer container) { - Iterator iter = iterator(); - Iterator toRetain = container.iterator(); - IColumn current = iter.hasNext() ? iter.next() : null; - IColumn retain = toRetain.hasNext() ? toRetain.next() : null; - AbstractType comparator = getComparator(); - while (current != null && retain != null) - { - int c = comparator.compare(current.name(), retain.name()); - if (c == 0) - { - if (current instanceof SuperColumn) - { - assert retain instanceof SuperColumn; - ((SuperColumn)current).retainAll((SuperColumn)retain); - } - current = iter.hasNext() ? iter.next() : null; - retain = toRetain.hasNext() ? toRetain.next() : null; - } - else if (c < 0) - { - iter.remove(); - current = iter.hasNext() ? iter.next() : null; - } - else // c > 0 - { - retain = toRetain.hasNext() ? toRetain.next() : null; - } - } - while (current != null) - { - iter.remove(); - current = iter.hasNext() ? iter.next() : null; - } + columns.retainAll(container.columns); } public int getColumnCount() @@ -249,23 +195,6 @@ public abstract class AbstractColumnContainer implements IColumnContainer, IIter return columns.reverseIterator(start); } - protected static class DeletionInfo - { - public final long markedForDeleteAt; - public final int localDeletionTime; - - public DeletionInfo() - { - this(Long.MIN_VALUE, Integer.MIN_VALUE); - } - - public DeletionInfo(long markedForDeleteAt, int localDeletionTime) - { - this.markedForDeleteAt = markedForDeleteAt; - this.localDeletionTime = localDeletionTime; - } - } - public boolean hasExpiredTombstones(int gcBefore) { if (isMarkedForDelete() && getLocalDeletionTime() < gcBefore) diff --git a/src/java/org/apache/cassandra/db/AbstractThreadUnsafeSortedColumns.java b/src/java/org/apache/cassandra/db/AbstractThreadUnsafeSortedColumns.java new file mode 100644 index 0000000000..256dc1e023 --- /dev/null +++ b/src/java/org/apache/cassandra/db/AbstractThreadUnsafeSortedColumns.java @@ -0,0 +1,111 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.db; + +import java.util.Iterator; + +import com.google.common.base.Function; + +import org.apache.cassandra.db.marshal.AbstractType; +import org.apache.cassandra.utils.Allocator; + +public abstract class AbstractThreadUnsafeSortedColumns implements ISortedColumns +{ + private DeletionInfo deletionInfo; + + public AbstractThreadUnsafeSortedColumns() + { + deletionInfo = new DeletionInfo(); + } + + public DeletionInfo getDeletionInfo() + { + return deletionInfo; + } + + public void delete(DeletionInfo newInfo) + { + if (deletionInfo.markedForDeleteAt < newInfo.markedForDeleteAt) + // since deletion info is immutable, aliasing it is fine + deletionInfo = newInfo; + } + + public void maybeResetDeletionTimes(int gcBefore) + { + // Update if it's not MIN_VALUE anymore and it has expired + if (deletionInfo.localDeletionTime != Integer.MIN_VALUE && deletionInfo.localDeletionTime <= gcBefore) + deletionInfo = new DeletionInfo(); + } + + public void retainAll(ISortedColumns columns) + { + Iterator iter = iterator(); + Iterator toRetain = columns.iterator(); + IColumn current = iter.hasNext() ? iter.next() : null; + IColumn retain = toRetain.hasNext() ? toRetain.next() : null; + AbstractType comparator = getComparator(); + while (current != null && retain != null) + { + int c = comparator.compare(current.name(), retain.name()); + if (c == 0) + { + if (current instanceof SuperColumn) + { + assert retain instanceof SuperColumn; + ((SuperColumn)current).retainAll((SuperColumn)retain); + } + current = iter.hasNext() ? iter.next() : null; + retain = toRetain.hasNext() ? toRetain.next() : null; + } + else if (c < 0) + { + iter.remove(); + current = iter.hasNext() ? iter.next() : null; + } + else // c > 0 + { + retain = toRetain.hasNext() ? toRetain.next() : null; + } + } + while (current != null) + { + iter.remove(); + current = iter.hasNext() ? iter.next() : null; + } + } + + // Implementations should implement this rather than addAll to avoid + // having to care about the deletion infos + protected abstract void addAllColumns(ISortedColumns columns, Allocator allocator, Function transformation); + + public void addAll(ISortedColumns columns, Allocator allocator, Function transformation) + { + addAllColumns(columns, allocator, transformation); + delete(columns.getDeletionInfo()); + } + + public boolean isEmpty() + { + return size() == 0; + } + + public int getEstimatedColumnCount() + { + return size(); + } +} diff --git a/src/java/org/apache/cassandra/db/ArrayBackedSortedColumns.java b/src/java/org/apache/cassandra/db/ArrayBackedSortedColumns.java index b049160565..f54cbc6899 100644 --- a/src/java/org/apache/cassandra/db/ArrayBackedSortedColumns.java +++ b/src/java/org/apache/cassandra/db/ArrayBackedSortedColumns.java @@ -20,6 +20,8 @@ package org.apache.cassandra.db; import java.nio.ByteBuffer; import java.util.*; +import com.google.common.base.Function; + import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.utils.Allocator; @@ -30,10 +32,11 @@ import org.apache.cassandra.utils.Allocator; * main operations performed are iterating over the map and adding columns * (especially if insertion is in sorted order). */ -public class ArrayBackedSortedColumns extends ArrayList implements ISortedColumns +public class ArrayBackedSortedColumns extends AbstractThreadUnsafeSortedColumns implements ISortedColumns { private final AbstractType comparator; private final boolean reversed; + private final ArrayList columns; public static final ISortedColumns.Factory factory = new Factory() { @@ -58,13 +61,14 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor super(); this.comparator = comparator; this.reversed = reversed; + this.columns = new ArrayList(); } private ArrayBackedSortedColumns(Collection columns, AbstractType comparator, boolean reversed) { - super(columns); this.comparator = comparator; this.reversed = reversed; + this.columns = new ArrayList(columns); } public ISortedColumns.Factory getFactory() @@ -79,7 +83,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor public ISortedColumns cloneMe() { - return new ArrayBackedSortedColumns(this, comparator, reversed); + return new ArrayBackedSortedColumns(columns, comparator, reversed); } public boolean isInsertReversed() @@ -98,7 +102,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor public IColumn getColumn(ByteBuffer name) { int pos = binarySearch(name); - return pos >= 0 ? get(pos) : null; + return pos >= 0 ? columns.get(pos) : null; } /** @@ -113,14 +117,14 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor */ public void addColumn(IColumn column, Allocator allocator) { - if (isEmpty()) + if (columns.isEmpty()) { - add(column); + columns.add(column); return; } // Fast path if inserting at the tail - int c = compare(get(size() - 1).name(), column.name()); + int c = compare(columns.get(size() - 1).name(), column.name()); // note that we want an assertion here (see addColumn javadoc), but we also want that if // assertion are disabled, addColumn works correctly with unsorted input assert c <= 0 : "Added column does not sort as the " + (reversed ? "first" : "last") + " column"; @@ -128,7 +132,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor if (c < 0) { // Insert as last - add(column); + columns.add(column); } else if (c == 0) { @@ -141,7 +145,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor if (pos >= 0) resolveAgainst(pos, column, allocator); else - add(-pos-1, column); + columns.add(-pos-1, column); } } @@ -151,7 +155,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor */ private void resolveAgainst(int i, IColumn column, Allocator allocator) { - IColumn oldColumn = get(i); + IColumn oldColumn = columns.get(i); if (oldColumn instanceof SuperColumn) { // Delegated to SuperColumn @@ -162,7 +166,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor { // calculate reconciled col from old (existing) col and new col IColumn reconciledColumn = column.reconcile(oldColumn, allocator); - set(i, reconciledColumn); + columns.set(i, reconciledColumn); } } @@ -181,7 +185,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor while (low <= high) { mid = (low + high) >> 1; - if ((result = compare(name, get(mid).name())) > 0) + if ((result = compare(name, columns.get(mid).name())) > 0) { low = mid + 1; } @@ -197,46 +201,46 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor return -mid - (result < 0 ? 1 : 2); } - public void addAll(ISortedColumns cm, Allocator allocator) + protected void addAllColumns(ISortedColumns cm, Allocator allocator, Function transformation) { if (cm.isEmpty()) return; - IColumn[] copy = toArray(new IColumn[size()]); + IColumn[] copy = columns.toArray(new IColumn[size()]); int idx = 0; Iterator other = reversed ? cm.reverseIterator() : cm.iterator(); IColumn otherColumn = other.next(); - clear(); + columns.clear(); while (idx < copy.length && otherColumn != null) { int c = compare(copy[idx].name(), otherColumn.name()); if (c < 0) { - add(copy[idx]); + columns.add(copy[idx]); idx++; } else if (c > 0) { - add(otherColumn); + columns.add(transformation.apply(otherColumn)); otherColumn = other.hasNext() ? other.next() : null; } else // c == 0 { - add(copy[idx]); - resolveAgainst(size() - 1, otherColumn, allocator); + columns.add(copy[idx]); + resolveAgainst(size() - 1, transformation.apply(otherColumn), allocator); idx++; otherColumn = other.hasNext() ? other.next() : null; } } while (idx < copy.length) { - add(copy[idx++]); + columns.add(copy[idx++]); } while (otherColumn != null) { - add(otherColumn); + columns.add(transformation.apply(otherColumn)); otherColumn = other.hasNext() ? other.next() : null; } } @@ -249,7 +253,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor int pos = binarySearch(oldColumn.name()); if (pos >= 0) { - set(pos, newColumn); + columns.set(pos, newColumn); } return pos >= 0; @@ -257,7 +261,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor public Collection getSortedColumns() { - return reversed ? new ReverseSortedCollection() : this; + return reversed ? new ReverseSortedCollection() : columns; } public Collection getReverseSortedColumns() @@ -272,7 +276,17 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor { int pos = binarySearch(name); if (pos >= 0) - remove(pos); + columns.remove(pos); + } + + public int size() + { + return columns.size(); + } + + public void clear() + { + columns.clear(); } public SortedSet getColumnNames() @@ -281,20 +295,15 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor return new ColumnNamesSet(); } - public int getEstimatedColumnCount() - { - return size(); - } - @Override public Iterator iterator() { - return reversed ? reverseInternalIterator(size()) : super.iterator(); + return reversed ? reverseInternalIterator(size()) : columns.iterator(); } public Iterator reverseIterator() { - return reversed ? super.iterator() : reverseInternalIterator(size()); + return reversed ? columns.iterator() : reverseInternalIterator(size()); } public Iterator iterator(ByteBuffer start) @@ -305,7 +314,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor else if (reversed) // listIterator.previous() doesn't return the current element at first but the previous one idx++; - return reversed ? reverseInternalIterator(idx) : listIterator(idx); + return reversed ? reverseInternalIterator(idx) : columns.listIterator(idx); } public Iterator reverseIterator(ByteBuffer start) @@ -316,12 +325,12 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor else if (!reversed) // listIterator.previous() doesn't return the current element at first but the previous one idx++; - return reversed ? listIterator(idx) : reverseInternalIterator(idx); + return reversed ? columns.listIterator(idx) : reverseInternalIterator(idx); } private Iterator reverseInternalIterator(int idx) { - final ListIterator iter = listIterator(idx); + final ListIterator iter = columns.listIterator(idx); return new Iterator() { public boolean hasNext() @@ -345,7 +354,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor { public int size() { - return ArrayBackedSortedColumns.this.size(); + return columns.size(); } public Iterator iterator() @@ -361,12 +370,12 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor public IColumn next() { - return ArrayBackedSortedColumns.this.get(idx--); + return columns.get(idx--); } public void remove() { - ArrayBackedSortedColumns.this.remove(idx--); + columns.remove(idx--); } }; } @@ -376,12 +385,12 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor { public int size() { - return ArrayBackedSortedColumns.this.size(); + return columns.size(); } public Iterator iterator() { - return ArrayBackedSortedColumns.super.iterator(); + return columns.iterator(); } } @@ -389,7 +398,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor { public int size() { - return ArrayBackedSortedColumns.this.size(); + return columns.size(); } public Iterator iterator() @@ -416,7 +425,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor public Comparator comparator() { - return ArrayBackedSortedColumns.this.getComparator(); + return getComparator(); } public ByteBuffer first() @@ -424,7 +433,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor final ArrayBackedSortedColumns outerList = ArrayBackedSortedColumns.this; if (outerList.isEmpty()) throw new NoSuchElementException(); - return outerList.get(outerList.reversed ? size() - 1 : 0).name(); + return outerList.columns.get(outerList.reversed ? size() - 1 : 0).name(); } public ByteBuffer last() @@ -432,7 +441,7 @@ public class ArrayBackedSortedColumns extends ArrayList implements ISor final ArrayBackedSortedColumns outerList = ArrayBackedSortedColumns.this; if (outerList.isEmpty()) throw new NoSuchElementException(); - return outerList.get(outerList.reversed ? 0 : size() - 1).name(); + return outerList.columns.get(outerList.reversed ? 0 : size() - 1).name(); } /* diff --git a/src/java/org/apache/cassandra/db/AtomicSortedColumns.java b/src/java/org/apache/cassandra/db/AtomicSortedColumns.java new file mode 100644 index 0000000000..6fd5bc750a --- /dev/null +++ b/src/java/org/apache/cassandra/db/AtomicSortedColumns.java @@ -0,0 +1,394 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.db; + +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +import com.google.common.base.Function; +import edu.stanford.ppl.concurrent.SnapTreeMap; + +import org.apache.cassandra.db.marshal.AbstractType; +import org.apache.cassandra.utils.Allocator; + + +/** + * A thread-safe and atomic ISortedColumns implementation. + * Operations (in particular addAll) on this implemenation are atomic and + * isolated (in the sense of ACID). Typically a addAll is guaranteed that no + * other thread can see the state where only parts but not all columns have + * been added. + * + * The implementation uses snaptree (https://github.com/nbronson/snaptree), + * and in particular it's copy-on-write clone operation to achieve its + * atomicity guarantee. + * + * WARNING: removing element through getSortedColumns().iterator() is *not* + * isolated of other operations and could actually be fully ignored in the + * face of a concurrent. Don't use it unless in a non-concurrent context. + * + * TODO: check the snaptree license make it ok to use + */ +public class AtomicSortedColumns implements ISortedColumns +{ + private final AtomicReference ref; + + public static final ISortedColumns.Factory factory = new Factory() + { + public ISortedColumns create(AbstractType comparator, boolean insertReversed) + { + return new AtomicSortedColumns(comparator); + } + + public ISortedColumns fromSorted(SortedMap sortedMap, boolean insertReversed) + { + return new AtomicSortedColumns(sortedMap); + } + }; + + public static ISortedColumns.Factory factory() + { + return factory; + } + + private AtomicSortedColumns(AbstractType comparator) + { + this(new Holder(comparator)); + } + + private AtomicSortedColumns(SortedMap columns) + { + this(new Holder(columns)); + } + + private AtomicSortedColumns(Holder holder) + { + this.ref = new AtomicReference(holder); + } + + public AbstractType getComparator() + { + return (AbstractType)ref.get().map.comparator(); + } + + public ISortedColumns.Factory getFactory() + { + return factory; + } + + public ISortedColumns cloneMe() + { + return new AtomicSortedColumns(ref.get().cloneMe()); + } + + public DeletionInfo getDeletionInfo() + { + return ref.get().deletionInfo; + } + + public void delete(DeletionInfo info) + { + // Keeping deletion info for max markedForDeleteAt value + Holder current; + do + { + current = ref.get(); + if (current.deletionInfo.markedForDeleteAt >= info.markedForDeleteAt) + break; + } + while (!ref.compareAndSet(current, current.with(info))); + } + + public void maybeResetDeletionTimes(int gcBefore) + { + Holder current; + do + { + current = ref.get(); + // Stop if we don't need to change the deletion info (it's still MIN_VALUE or not expired yet) + if (current.deletionInfo.localDeletionTime == Integer.MIN_VALUE || current.deletionInfo.localDeletionTime > gcBefore) + break; + } + while (!ref.compareAndSet(current, current.with(new DeletionInfo()))); + } + + public void retainAll(ISortedColumns columns) + { + Holder current, modified; + do + { + current = ref.get(); + modified = current.cloneMe(); + modified.retainAll(columns); + } + while (!ref.compareAndSet(current, modified)); + } + + public void addColumn(IColumn column, Allocator allocator) + { + Holder current, modified; + do + { + current = ref.get(); + modified = current.cloneMe(); + modified.addColumn(column, allocator); + } + while (!ref.compareAndSet(current, modified)); + } + + public void addAll(ISortedColumns cm, Allocator allocator, Function transformation) + { + /* + * This operation needs to atomicity and isolation. To that end, we + * add the new column to a copy of the map (a cheap O(1) snapTree + * clone) and atomically compare and swap when everything has been + * added. Of course, we must not forget to update the deletion times + * too. + * In case we are adding a lot of columns, failing the final compare + * and swap could be expensive. To mitigate, we check we haven't been + * beaten by another thread after every column addition. If we have, + * we bail early, avoiding unnecessary work if possible. + */ + Holder current, modified; + main_loop: + do + { + current = ref.get(); + DeletionInfo newDelInfo = current.deletionInfo; + if (newDelInfo.markedForDeleteAt < cm.getDeletionInfo().markedForDeleteAt) + newDelInfo = cm.getDeletionInfo(); + modified = new Holder(current.map.clone(), newDelInfo); + + for (IColumn column : cm.getSortedColumns()) + { + modified.addColumn(transformation.apply(column), allocator); + // bail early if we know we've been beaten + if (ref.get() != current) + continue main_loop; + } + } + while (!ref.compareAndSet(current, modified)); + } + + public boolean replace(IColumn oldColumn, IColumn newColumn) + { + if (!oldColumn.name().equals(newColumn.name())) + throw new IllegalArgumentException(); + + Holder current, modified; + boolean replaced; + do + { + current = ref.get(); + modified = current.cloneMe(); + replaced = modified.map.replace(oldColumn.name(), oldColumn, newColumn); + } + while (!ref.compareAndSet(current, modified)); + return replaced; + } + + public void removeColumn(ByteBuffer name) + { + Holder current, modified; + do + { + current = ref.get(); + modified = current.cloneMe(); + modified.map.remove(name); + } + while (!ref.compareAndSet(current, modified)); + } + + public void clear() + { + Holder current, modified; + do + { + current = ref.get(); + modified = current.clear(); + } + while (!ref.compareAndSet(current, modified)); + } + + public IColumn getColumn(ByteBuffer name) + { + return ref.get().map.get(name); + } + + public SortedSet getColumnNames() + { + return ref.get().map.keySet(); + } + + public Collection getSortedColumns() + { + return ref.get().map.values(); + } + + public Collection getReverseSortedColumns() + { + return ref.get().map.descendingMap().values(); + } + + public int size() + { + return ref.get().map.size(); + } + + public int getEstimatedColumnCount() + { + return size(); + } + + public boolean isEmpty() + { + return ref.get().map.isEmpty(); + } + + public Iterator iterator() + { + return getSortedColumns().iterator(); + } + + public Iterator reverseIterator() + { + return getReverseSortedColumns().iterator(); + } + + public Iterator iterator(ByteBuffer start) + { + return ref.get().map.tailMap(start).values().iterator(); + } + + public Iterator reverseIterator(ByteBuffer start) + { + return ref.get().map.descendingMap().tailMap(start).values().iterator(); + } + + public boolean isInsertReversed() + { + return false; + } + + private static class Holder + { + final SnapTreeMap map; + final DeletionInfo deletionInfo; + + Holder(AbstractType comparator) + { + this(new SnapTreeMap(comparator), new DeletionInfo()); + } + + Holder(SortedMap columns) + { + this(new SnapTreeMap(columns), new DeletionInfo()); + } + + Holder(SnapTreeMap map, DeletionInfo deletionInfo) + { + this.map = map; + this.deletionInfo = deletionInfo; + } + + Holder cloneMe() + { + return with(map.clone()); + } + + Holder with(DeletionInfo info) + { + return new Holder(map, info); + } + + Holder with(SnapTreeMap newMap) + { + return new Holder(newMap, deletionInfo); + } + + // There is no point in cloning the underlying map to clear it + // afterwards. + Holder clear() + { + return new Holder(new SnapTreeMap(map.comparator()), deletionInfo); + } + + void addColumn(IColumn column, Allocator allocator) + { + ByteBuffer name = column.name(); + IColumn oldColumn; + while ((oldColumn = map.putIfAbsent(name, column)) != null) + { + if (oldColumn instanceof SuperColumn) + { + assert column instanceof SuperColumn; + ((SuperColumn) oldColumn).putColumn((SuperColumn)column, allocator); + break; // Delegated to SuperColumn + } + else + { + // calculate reconciled col from old (existing) col and new col + IColumn reconciledColumn = column.reconcile(oldColumn, allocator); + if (map.replace(name, oldColumn, reconciledColumn)) + break; + + // We failed to replace column due to a concurrent update or a concurrent removal. Keep trying. + // (Currently, concurrent removal should not happen (only updates), but let us support that anyway.) + } + } + } + + void retainAll(ISortedColumns columns) + { + Iterator iter = map.values().iterator(); + Iterator toRetain = columns.iterator(); + IColumn current = iter.hasNext() ? iter.next() : null; + IColumn retain = toRetain.hasNext() ? toRetain.next() : null; + Comparator comparator = map.comparator(); + while (current != null && retain != null) + { + int c = comparator.compare(current.name(), retain.name()); + if (c == 0) + { + if (current instanceof SuperColumn) + { + assert retain instanceof SuperColumn; + ((SuperColumn)current).retainAll((SuperColumn)retain); + } + current = iter.hasNext() ? iter.next() : null; + retain = toRetain.hasNext() ? toRetain.next() : null; + } + else if (c < 0) + { + iter.remove(); + current = iter.hasNext() ? iter.next() : null; + } + else // c > 0 + { + retain = toRetain.hasNext() ? toRetain.next() : null; + } + } + while (current != null) + { + iter.remove(); + current = iter.hasNext() ? iter.next() : null; + } + } + } +} diff --git a/src/java/org/apache/cassandra/db/CollationController.java b/src/java/org/apache/cassandra/db/CollationController.java index f7a65c0df0..dd7464dfc3 100644 --- a/src/java/org/apache/cassandra/db/CollationController.java +++ b/src/java/org/apache/cassandra/db/CollationController.java @@ -76,7 +76,7 @@ public class CollationController logger.debug("collectTimeOrderedData"); ISortedColumns.Factory factory = mutableColumns - ? ThreadSafeSortedColumns.factory() + ? AtomicSortedColumns.factory() : TreeMapBackedSortedColumns.factory(); ColumnFamily container = ColumnFamily.create(cfs.metadata, factory, filter.filter.isReversed()); List iterators = new ArrayList(); @@ -209,7 +209,7 @@ public class CollationController { logger.debug("collectAllData"); ISortedColumns.Factory factory = mutableColumns - ? ThreadSafeSortedColumns.factory() + ? AtomicSortedColumns.factory() : ArrayBackedSortedColumns.factory(); List iterators = new ArrayList(); ColumnFamily returnCF = ColumnFamily.create(cfs.metadata, factory, filter.filter.isReversed()); diff --git a/src/java/org/apache/cassandra/db/ColumnFamily.java b/src/java/org/apache/cassandra/db/ColumnFamily.java index 2337dfcc24..af089cbeeb 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamily.java +++ b/src/java/org/apache/cassandra/db/ColumnFamily.java @@ -61,7 +61,7 @@ public class ColumnFamily extends AbstractColumnContainer public static ColumnFamily create(CFMetaData cfm) { - return create(cfm, ThreadSafeSortedColumns.factory()); + return create(cfm, TreeMapBackedSortedColumns.factory()); } public static ColumnFamily create(CFMetaData cfm, ISortedColumns.Factory factory) @@ -84,8 +84,7 @@ public class ColumnFamily extends AbstractColumnContainer public ColumnFamily cloneMeShallow(ISortedColumns.Factory factory, boolean reversedInsertOrder) { ColumnFamily cf = ColumnFamily.create(cfm, factory, reversedInsertOrder); - // since deletion info is immutable, aliasing it is fine - cf.deletionInfo.set(deletionInfo.get()); + cf.delete(this); return cf; } @@ -108,8 +107,7 @@ public class ColumnFamily extends AbstractColumnContainer public ColumnFamily cloneMe() { ColumnFamily cf = new ColumnFamily(cfm, columns.cloneMe()); - // since deletion info is immutable, aliasing it is fine - cf.deletionInfo.set(deletionInfo.get()); + cf.delete(this); return cf; } diff --git a/src/java/org/apache/cassandra/db/ColumnFamilySerializer.java b/src/java/org/apache/cassandra/db/ColumnFamilySerializer.java index 7f11948b0b..dbe2379b57 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamilySerializer.java +++ b/src/java/org/apache/cassandra/db/ColumnFamilySerializer.java @@ -115,7 +115,7 @@ public class ColumnFamilySerializer implements ISerializer public ColumnFamily deserialize(DataInput dis) throws IOException { - return deserialize(dis, IColumnSerializer.Flag.LOCAL, ThreadSafeSortedColumns.factory()); + return deserialize(dis, IColumnSerializer.Flag.LOCAL, TreeMapBackedSortedColumns.factory()); } public ColumnFamily deserialize(DataInput dis, IColumnSerializer.Flag flag, ISortedColumns.Factory factory) throws IOException diff --git a/src/java/org/apache/cassandra/db/ISortedColumns.java b/src/java/org/apache/cassandra/db/ISortedColumns.java index 75506ddf37..63201129aa 100644 --- a/src/java/org/apache/cassandra/db/ISortedColumns.java +++ b/src/java/org/apache/cassandra/db/ISortedColumns.java @@ -23,6 +23,8 @@ import java.util.Iterator; import java.util.SortedMap; import java.util.SortedSet; +import com.google.common.base.Function; + import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.io.util.IIterableColumns; import org.apache.cassandra.utils.Allocator; @@ -46,6 +48,11 @@ public interface ISortedColumns extends IIterableColumns */ public Factory getFactory(); + public DeletionInfo getDeletionInfo(); + public void delete(DeletionInfo info); + public void maybeResetDeletionTimes(int gcBefore); + public void retainAll(ISortedColumns columns); + /** * Adds a column to this column map. * If a column with the same name is already present in the map, it will @@ -62,7 +69,7 @@ public interface ISortedColumns extends IIterableColumns * * but is potentially faster. */ - public void addAll(ISortedColumns cm, Allocator allocator); + public void addAll(ISortedColumns cm, Allocator allocator, Function transformation); /** * Replace oldColumn if present by newColumn. @@ -161,4 +168,22 @@ public interface ISortedColumns extends IIterableColumns */ public ISortedColumns fromSorted(SortedMap sm, boolean insertReversed); } + + public static class DeletionInfo + { + public final long markedForDeleteAt; + public final int localDeletionTime; + + public DeletionInfo() + { + this(Long.MIN_VALUE, Integer.MIN_VALUE); + } + + public DeletionInfo(long markedForDeleteAt, int localDeletionTime) + { + this.markedForDeleteAt = markedForDeleteAt; + this.localDeletionTime = localDeletionTime; + } + } + } diff --git a/src/java/org/apache/cassandra/db/Memtable.java b/src/java/org/apache/cassandra/db/Memtable.java index 78d69edb25..bf8382e356 100644 --- a/src/java/org/apache/cassandra/db/Memtable.java +++ b/src/java/org/apache/cassandra/db/Memtable.java @@ -25,6 +25,7 @@ import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; +import com.google.common.base.Function; import com.google.common.collect.Iterators; import com.google.common.collect.PeekingIterator; import org.slf4j.Logger; @@ -82,7 +83,15 @@ public class Memtable public final ColumnFamilyStore cfs; private final long creationTime; - private SlabAllocator allocator = new SlabAllocator(); + private final SlabAllocator allocator = new SlabAllocator(); + // We really only need one column by allocator but one by memtable is not a big waste and avoids needing allocators to know about CFS + private final Function localCopyFunction = new Function() + { + public IColumn apply(IColumn c) + { + return c.localCopy(cfs, allocator); + }; + }; public Memtable(ColumnFamilyStore cfs) { @@ -203,25 +212,19 @@ public class Memtable ? cf.isMarkedForDelete() ? 1 : 0 : cf.getColumnCount()); - ColumnFamily clonedCf = columnFamilies.get(key); - // if the row doesn't exist yet in the memtable, clone cf to our allocator. - if (clonedCf == null) + + ColumnFamily previous = columnFamilies.get(key); + + if (previous == null) { - clonedCf = cf.cloneMeShallow(); - for (IColumn column : cf.getSortedColumns()) - clonedCf.addColumn(column.localCopy(cfs, allocator)); - clonedCf = columnFamilies.putIfAbsent(new DecoratedKey(key.token, allocator.clone(key.key)), clonedCf); - if (clonedCf == null) - return; - // else there was a race and the other thread won. fall through to updating his CF object + ColumnFamily empty = cf.cloneMeShallow(AtomicSortedColumns.factory(), false); + // We'll add the columns later. This avoids wasting works if we get beaten in the putIfAbsent + previous = columnFamilies.putIfAbsent(new DecoratedKey(key.token, allocator.clone(key.key)), empty); + if (previous == null) + previous = empty; } - // we duplicate the funcationality of CF.resolve here to avoid having to either pass the Memtable in for - // the cloning operation, or cloning the CF container as well as the Columns. fortunately, resolve - // is really quite simple: - clonedCf.delete(cf); - for (IColumn column : cf.getSortedColumns()) - clonedCf.addColumn(column.localCopy(cfs, allocator), allocator); + previous.addAll(cf, allocator, localCopyFunction); } // for debugging diff --git a/src/java/org/apache/cassandra/db/Row.java b/src/java/org/apache/cassandra/db/Row.java index 523f501834..81cfc7b674 100644 --- a/src/java/org/apache/cassandra/db/Row.java +++ b/src/java/org/apache/cassandra/db/Row.java @@ -70,7 +70,7 @@ public class Row public Row deserialize(DataInput dis, int version) throws IOException { - return deserialize(dis, version, IColumnSerializer.Flag.LOCAL, ThreadSafeSortedColumns.factory()); + return deserialize(dis, version, IColumnSerializer.Flag.LOCAL, TreeMapBackedSortedColumns.factory()); } public long serializedSize(Row row, int version) diff --git a/src/java/org/apache/cassandra/db/RowMutation.java b/src/java/org/apache/cassandra/db/RowMutation.java index 443ca94a6b..4a58ad7b04 100644 --- a/src/java/org/apache/cassandra/db/RowMutation.java +++ b/src/java/org/apache/cassandra/db/RowMutation.java @@ -398,7 +398,7 @@ public class RowMutation implements IMutation, MessageProducer for (int i = 0; i < size; ++i) { Integer cfid = Integer.valueOf(dis.readInt()); - ColumnFamily cf = ColumnFamily.serializer().deserialize(dis, flag, ThreadSafeSortedColumns.factory()); + ColumnFamily cf = ColumnFamily.serializer().deserialize(dis, flag, TreeMapBackedSortedColumns.factory()); modifications.put(cfid, cf); } return new RowMutation(table, key, modifications); diff --git a/src/java/org/apache/cassandra/db/SuperColumn.java b/src/java/org/apache/cassandra/db/SuperColumn.java index 4301f7312e..ab04d6333f 100644 --- a/src/java/org/apache/cassandra/db/SuperColumn.java +++ b/src/java/org/apache/cassandra/db/SuperColumn.java @@ -55,7 +55,7 @@ public class SuperColumn extends AbstractColumnContainer implements IColumn public SuperColumn(ByteBuffer name, AbstractType comparator) { - this(name, ThreadSafeSortedColumns.factory().create(comparator, false)); + this(name, AtomicSortedColumns.factory().create(comparator, false)); } SuperColumn(ByteBuffer name, ISortedColumns columns) @@ -69,16 +69,14 @@ public class SuperColumn extends AbstractColumnContainer implements IColumn public SuperColumn cloneMeShallow() { SuperColumn sc = new SuperColumn(name, getComparator()); - // since deletion info is immutable, aliasing it is fine - sc.deletionInfo.set(deletionInfo.get()); + sc.delete(this); return sc; } public IColumn cloneMe() { SuperColumn sc = new SuperColumn(name, columns.cloneMe()); - // since deletion info is immutable, aliasing it is fine - sc.deletionInfo.set(deletionInfo.get()); + sc.delete(this); return sc; } @@ -262,8 +260,7 @@ public class SuperColumn extends AbstractColumnContainer implements IColumn // we don't try to intern supercolumn names, because if we're using Cassandra correctly it's almost // certainly just going to pollute our interning map with unique, dynamic values SuperColumn sc = new SuperColumn(allocator.clone(name), this.getComparator()); - // since deletion info is immutable, aliasing it is fine - sc.deletionInfo.set(deletionInfo.get()); + sc.delete(this); for(IColumn c : columns) { @@ -358,7 +355,7 @@ class SuperColumnSerializer implements IColumnSerializer int size = dis.readInt(); ColumnSerializer serializer = Column.serializer(); ColumnSortedMap preSortedMap = new ColumnSortedMap(comparator, serializer, dis, size, flag, expireBefore); - SuperColumn superColumn = new SuperColumn(name, ThreadSafeSortedColumns.factory().fromSorted(preSortedMap, false)); + SuperColumn superColumn = new SuperColumn(name, AtomicSortedColumns.factory().fromSorted(preSortedMap, false)); superColumn.delete(localDeleteTime, markedForDeleteAt); return superColumn; } diff --git a/src/java/org/apache/cassandra/db/ThreadSafeSortedColumns.java b/src/java/org/apache/cassandra/db/ThreadSafeSortedColumns.java deleted file mode 100644 index 4bb3235df7..0000000000 --- a/src/java/org/apache/cassandra/db/ThreadSafeSortedColumns.java +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.cassandra.db; - -import java.nio.ByteBuffer; -import java.util.Collection; -import java.util.Iterator; -import java.util.SortedMap; -import java.util.SortedSet; -import java.util.concurrent.ConcurrentSkipListMap; - -import org.apache.cassandra.db.marshal.AbstractType; -import org.apache.cassandra.utils.Allocator; - -public class ThreadSafeSortedColumns extends ConcurrentSkipListMap implements ISortedColumns -{ - public static final ISortedColumns.Factory factory = new Factory() - { - public ISortedColumns create(AbstractType comparator, boolean insertReversed) - { - return new ThreadSafeSortedColumns(comparator); - } - - public ISortedColumns fromSorted(SortedMap sortedMap, boolean insertReversed) - { - return new ThreadSafeSortedColumns(sortedMap); - } - }; - - public static ISortedColumns.Factory factory() - { - return factory; - } - - public AbstractType getComparator() - { - return (AbstractType)comparator(); - } - - private ThreadSafeSortedColumns(AbstractType comparator) - { - super(comparator); - } - - private ThreadSafeSortedColumns(SortedMap columns) - { - super(columns); - } - - public ISortedColumns.Factory getFactory() - { - return factory(); - } - - public ISortedColumns cloneMe() - { - return new ThreadSafeSortedColumns(this); - } - - public boolean isInsertReversed() - { - return false; - } - - /* - * If we find an old column that has the same name - * the ask it to resolve itself else add the new column - */ - public void addColumn(IColumn column, Allocator allocator) - { - ByteBuffer name = column.name(); - IColumn oldColumn; - while ((oldColumn = putIfAbsent(name, column)) != null) - { - if (oldColumn instanceof SuperColumn) - { - assert column instanceof SuperColumn; - ((SuperColumn) oldColumn).putColumn((SuperColumn)column, allocator); - break; // Delegated to SuperColumn - } - else - { - // calculate reconciled col from old (existing) col and new col - IColumn reconciledColumn = column.reconcile(oldColumn, allocator); - if (replace(name, oldColumn, reconciledColumn)) - break; - - // We failed to replace column due to a concurrent update or a concurrent removal. Keep trying. - // (Currently, concurrent removal should not happen (only updates), but let us support that anyway.) - } - } - } - - /** - * We need to go through each column in the column container and resolve it before adding - */ - public void addAll(ISortedColumns cm, Allocator allocator) - { - for (IColumn column : cm.getSortedColumns()) - addColumn(column, allocator); - } - - public boolean replace(IColumn oldColumn, IColumn newColumn) - { - if (!oldColumn.name().equals(newColumn.name())) - throw new IllegalArgumentException(); - - return replace(oldColumn.name(), oldColumn, newColumn); - } - - public IColumn getColumn(ByteBuffer name) - { - return get(name); - } - - public void removeColumn(ByteBuffer name) - { - remove(name); - } - - public Collection getSortedColumns() - { - return values(); - } - - public Collection getReverseSortedColumns() - { - return descendingMap().values(); - } - - public SortedSet getColumnNames() - { - return keySet(); - } - - public int getEstimatedColumnCount() - { - return size(); - } - - public Iterator iterator() - { - return values().iterator(); - } - - public Iterator reverseIterator() - { - return getReverseSortedColumns().iterator(); - } - - public Iterator iterator(ByteBuffer start) - { - return tailMap(start).values().iterator(); - } - - public Iterator reverseIterator(ByteBuffer start) - { - return descendingMap().tailMap(start).values().iterator(); - } -} diff --git a/src/java/org/apache/cassandra/db/TreeMapBackedSortedColumns.java b/src/java/org/apache/cassandra/db/TreeMapBackedSortedColumns.java index f126508cf7..5dff781ccd 100644 --- a/src/java/org/apache/cassandra/db/TreeMapBackedSortedColumns.java +++ b/src/java/org/apache/cassandra/db/TreeMapBackedSortedColumns.java @@ -24,11 +24,15 @@ import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; +import com.google.common.base.Function; + import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.utils.Allocator; -public class TreeMapBackedSortedColumns extends TreeMap implements ISortedColumns +public class TreeMapBackedSortedColumns extends AbstractThreadUnsafeSortedColumns implements ISortedColumns { + private final TreeMap map; + public static final ISortedColumns.Factory factory = new Factory() { public ISortedColumns create(AbstractType comparator, boolean insertReversed) @@ -49,17 +53,17 @@ public class TreeMapBackedSortedColumns extends TreeMap imp public AbstractType getComparator() { - return (AbstractType)comparator(); + return (AbstractType)map.comparator(); } private TreeMapBackedSortedColumns(AbstractType comparator) { - super(comparator); + this.map = new TreeMap(comparator); } private TreeMapBackedSortedColumns(SortedMap columns) { - super(columns); + this.map = new TreeMap(columns); } public ISortedColumns.Factory getFactory() @@ -69,7 +73,7 @@ public class TreeMapBackedSortedColumns extends TreeMap imp public ISortedColumns cloneMe() { - return new TreeMapBackedSortedColumns(this); + return new TreeMapBackedSortedColumns(map); } public boolean isInsertReversed() @@ -88,7 +92,7 @@ public class TreeMapBackedSortedColumns extends TreeMap imp // but TreeMap lacks putAbsent. Rather than split it into a "get, then put" check, we do it as follows, // which saves the extra "get" in the no-conflict case [for both normal and super columns], // in exchange for a re-put in the SuperColumn case. - IColumn oldColumn = put(name, column); + IColumn oldColumn = map.put(name, column); if (oldColumn != null) { if (oldColumn instanceof SuperColumn) @@ -98,13 +102,13 @@ public class TreeMapBackedSortedColumns extends TreeMap imp // add the new one to the old, then place old back in the Map, rather than copy the old contents // into the new Map entry. ((SuperColumn) oldColumn).putColumn((SuperColumn)column, allocator); - put(name, oldColumn); + map.put(name, oldColumn); } else { // calculate reconciled col from old (existing) col and new col IColumn reconciledColumn = column.reconcile(oldColumn, allocator); - put(name, reconciledColumn); + map.put(name, reconciledColumn); } } } @@ -112,10 +116,10 @@ public class TreeMapBackedSortedColumns extends TreeMap imp /** * We need to go through each column in the column container and resolve it before adding */ - public void addAll(ISortedColumns cm, Allocator allocator) + protected void addAllColumns(ISortedColumns cm, Allocator allocator, Function transformation) { for (IColumn column : cm.getSortedColumns()) - addColumn(column, allocator); + addColumn(transformation.apply(column), allocator); } public boolean replace(IColumn oldColumn, IColumn newColumn) @@ -127,15 +131,15 @@ public class TreeMapBackedSortedColumns extends TreeMap imp // column or the column was not equal to oldColumn (to be coherent // with other implementation). We optimize for the common case where // oldColumn do is present though. - IColumn previous = put(oldColumn.name(), newColumn); + IColumn previous = map.put(oldColumn.name(), newColumn); if (previous == null) { - remove(oldColumn.name()); + map.remove(oldColumn.name()); return false; } if (!previous.equals(oldColumn)) { - put(oldColumn.name(), previous); + map.put(oldColumn.name(), previous); return false; } return true; @@ -143,37 +147,42 @@ public class TreeMapBackedSortedColumns extends TreeMap imp public IColumn getColumn(ByteBuffer name) { - return get(name); + return map.get(name); } public void removeColumn(ByteBuffer name) { - remove(name); + map.remove(name); + } + + public void clear() + { + map.clear(); + } + + public int size() + { + return map.size(); } public Collection getSortedColumns() { - return values(); + return map.values(); } public Collection getReverseSortedColumns() { - return descendingMap().values(); + return map.descendingMap().values(); } public SortedSet getColumnNames() { - return navigableKeySet(); - } - - public int getEstimatedColumnCount() - { - return size(); + return map.navigableKeySet(); } public Iterator iterator() { - return values().iterator(); + return map.values().iterator(); } public Iterator reverseIterator() @@ -183,11 +192,11 @@ public class TreeMapBackedSortedColumns extends TreeMap imp public Iterator iterator(ByteBuffer start) { - return tailMap(start).values().iterator(); + return map.tailMap(start).values().iterator(); } public Iterator reverseIterator(ByteBuffer start) { - return descendingMap().tailMap(start).values().iterator(); + return map.descendingMap().tailMap(start).values().iterator(); } } diff --git a/test/unit/org/apache/cassandra/db/ArrayBackedSortedColumnsTest.java b/test/unit/org/apache/cassandra/db/ArrayBackedSortedColumnsTest.java index bec6559bba..8cfbb21c8a 100644 --- a/test/unit/org/apache/cassandra/db/ArrayBackedSortedColumnsTest.java +++ b/test/unit/org/apache/cassandra/db/ArrayBackedSortedColumnsTest.java @@ -28,6 +28,8 @@ import org.junit.Test; import static org.junit.Assert.*; +import com.google.common.base.Function; + import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.db.marshal.BytesType; import org.apache.cassandra.utils.HeapAllocator; @@ -76,7 +78,7 @@ public class ArrayBackedSortedColumnsTest for (int i = 0; i < values2.length; ++i) map2.addColumn(new Column(ByteBufferUtil.bytes(values2[reversed ? values2.length - 1 - i : i])), HeapAllocator.instance); - map2.addAll(map, HeapAllocator.instance); + map2.addAll(map, HeapAllocator.instance, new Function(){ public IColumn apply(IColumn c) { return c; }; }); Iterator iter = map2.iterator(); assertEquals("1st column", 1, iter.next().name().getInt(0)); diff --git a/test/unit/org/apache/cassandra/db/ColumnFamilyTest.java b/test/unit/org/apache/cassandra/db/ColumnFamilyTest.java index 2d2658fcad..18619a1944 100644 --- a/test/unit/org/apache/cassandra/db/ColumnFamilyTest.java +++ b/test/unit/org/apache/cassandra/db/ColumnFamilyTest.java @@ -174,7 +174,7 @@ public class ColumnFamilyTest extends SchemaLoader public void testSuperColumnResolution() { testSuperColumnResolution(TreeMapBackedSortedColumns.factory()); - testSuperColumnResolution(ThreadSafeSortedColumns.factory()); + testSuperColumnResolution(AtomicSortedColumns.factory()); // array-sorted does allow conflict resolution IF it is the last column. Bit of an edge case. testSuperColumnResolution(ArrayBackedSortedColumns.factory()); }