cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From slebre...@apache.org
Subject svn commit: r1152799 - in /cassandra/trunk: ./ contrib/ interface/thrift/gen-java/org/apache/cassandra/thrift/ src/java/org/apache/cassandra/db/commitlog/ test/unit/org/apache/cassandra/db/
Date Mon, 01 Aug 2011 14:31:37 GMT
Author: slebresne
Date: Mon Aug  1 14:31:36 2011
New Revision: 1152799

URL: http://svn.apache.org/viewvc?rev=1152799&view=rev
Log:
Merge from 0.8

Modified:
    cassandra/trunk/   (props changed)
    cassandra/trunk/CHANGES.txt
    cassandra/trunk/contrib/   (props changed)
    cassandra/trunk/interface/thrift/gen-java/org/apache/cassandra/thrift/Cassandra.java 
 (props changed)
    cassandra/trunk/interface/thrift/gen-java/org/apache/cassandra/thrift/Column.java   (props
changed)
    cassandra/trunk/interface/thrift/gen-java/org/apache/cassandra/thrift/InvalidRequestException.java
  (props changed)
    cassandra/trunk/interface/thrift/gen-java/org/apache/cassandra/thrift/NotFoundException.java
  (props changed)
    cassandra/trunk/interface/thrift/gen-java/org/apache/cassandra/thrift/SuperColumn.java
  (props changed)
    cassandra/trunk/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
    cassandra/trunk/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
    cassandra/trunk/test/unit/org/apache/cassandra/db/CommitLogTest.java

Propchange: cassandra/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Aug  1 14:31:36 2011
@@ -1,7 +1,7 @@
 /cassandra/branches/cassandra-0.6:922689-1052356,1052358-1053452,1053454,1053456-1131291
 /cassandra/branches/cassandra-0.7:1026516-1151306
 /cassandra/branches/cassandra-0.7.0:1053690-1055654
-/cassandra/branches/cassandra-0.8:1090934-1125013,1125019-1152110,1152265
+/cassandra/branches/cassandra-0.8:1090934-1125013,1125019-1152110,1152265,1152793
 /cassandra/branches/cassandra-0.8.0:1125021-1130369
 /cassandra/branches/cassandra-0.8.1:1101014-1125018
 /cassandra/tags/cassandra-0.7.0-rc3:1051699-1053689

Modified: cassandra/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/cassandra/trunk/CHANGES.txt?rev=1152799&r1=1152798&r2=1152799&view=diff
==============================================================================
--- cassandra/trunk/CHANGES.txt (original)
+++ cassandra/trunk/CHANGES.txt Mon Aug  1 14:31:36 2011
@@ -39,6 +39,8 @@
  * keep gossipped version in sync with actual on migration coordinator 
    (CASSANDRA-2946)
  * speedup bytes to hex conversions dramatically (CASSANDRA-2850)
+ * fix bug where dirty commit logs were removed (and avoid keeping segment
+   with no post-flush activity permanently dirty) (CASSANDRA-2829)
 
 
 0.8.2

Propchange: cassandra/trunk/contrib/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Aug  1 14:31:36 2011
@@ -1,7 +1,7 @@
 /cassandra/branches/cassandra-0.6/contrib:922689-1052356,1052358-1053452,1053454,1053456-1068009
 /cassandra/branches/cassandra-0.7/contrib:1026516-1151306
 /cassandra/branches/cassandra-0.7.0/contrib:1053690-1055654
-/cassandra/branches/cassandra-0.8/contrib:1090934-1125013,1125019-1152110,1152265
+/cassandra/branches/cassandra-0.8/contrib:1090934-1125013,1125019-1152110,1152265,1152793
 /cassandra/branches/cassandra-0.8.0/contrib:1125021-1130369
 /cassandra/branches/cassandra-0.8.1/contrib:1101014-1125018
 /cassandra/tags/cassandra-0.7.0-rc3/contrib:1051699-1053689

Propchange: cassandra/trunk/interface/thrift/gen-java/org/apache/cassandra/thrift/Cassandra.java
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Aug  1 14:31:36 2011
@@ -1,7 +1,7 @@
 /cassandra/branches/cassandra-0.6/interface/thrift/gen-java/org/apache/cassandra/thrift/Cassandra.java:922689-1052356,1052358-1053452,1053454,1053456-1131291
 /cassandra/branches/cassandra-0.7/interface/thrift/gen-java/org/apache/cassandra/thrift/Cassandra.java:1026516-1151306
 /cassandra/branches/cassandra-0.7.0/interface/thrift/gen-java/org/apache/cassandra/thrift/Cassandra.java:1053690-1055654
-/cassandra/branches/cassandra-0.8/interface/thrift/gen-java/org/apache/cassandra/thrift/Cassandra.java:1090934-1125013,1125019-1152110,1152265
+/cassandra/branches/cassandra-0.8/interface/thrift/gen-java/org/apache/cassandra/thrift/Cassandra.java:1090934-1125013,1125019-1152110,1152265,1152793
 /cassandra/branches/cassandra-0.8.0/interface/thrift/gen-java/org/apache/cassandra/thrift/Cassandra.java:1125021-1130369
 /cassandra/branches/cassandra-0.8.1/interface/thrift/gen-java/org/apache/cassandra/thrift/Cassandra.java:1101014-1125018
 /cassandra/tags/cassandra-0.7.0-rc3/interface/thrift/gen-java/org/apache/cassandra/thrift/Cassandra.java:1051699-1053689

Propchange: cassandra/trunk/interface/thrift/gen-java/org/apache/cassandra/thrift/Column.java
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Aug  1 14:31:36 2011
@@ -1,7 +1,7 @@
 /cassandra/branches/cassandra-0.6/interface/thrift/gen-java/org/apache/cassandra/thrift/Column.java:922689-1052356,1052358-1053452,1053454,1053456-1131291
 /cassandra/branches/cassandra-0.7/interface/thrift/gen-java/org/apache/cassandra/thrift/Column.java:1026516-1151306
 /cassandra/branches/cassandra-0.7.0/interface/thrift/gen-java/org/apache/cassandra/thrift/Column.java:1053690-1055654
-/cassandra/branches/cassandra-0.8/interface/thrift/gen-java/org/apache/cassandra/thrift/Column.java:1090934-1125013,1125019-1152110,1152265
+/cassandra/branches/cassandra-0.8/interface/thrift/gen-java/org/apache/cassandra/thrift/Column.java:1090934-1125013,1125019-1152110,1152265,1152793
 /cassandra/branches/cassandra-0.8.0/interface/thrift/gen-java/org/apache/cassandra/thrift/Column.java:1125021-1130369
 /cassandra/branches/cassandra-0.8.1/interface/thrift/gen-java/org/apache/cassandra/thrift/Column.java:1101014-1125018
 /cassandra/tags/cassandra-0.7.0-rc3/interface/thrift/gen-java/org/apache/cassandra/thrift/Column.java:1051699-1053689

Propchange: cassandra/trunk/interface/thrift/gen-java/org/apache/cassandra/thrift/InvalidRequestException.java
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Aug  1 14:31:36 2011
@@ -1,7 +1,7 @@
 /cassandra/branches/cassandra-0.6/interface/thrift/gen-java/org/apache/cassandra/thrift/InvalidRequestException.java:922689-1052356,1052358-1053452,1053454,1053456-1131291
 /cassandra/branches/cassandra-0.7/interface/thrift/gen-java/org/apache/cassandra/thrift/InvalidRequestException.java:1026516-1151306
 /cassandra/branches/cassandra-0.7.0/interface/thrift/gen-java/org/apache/cassandra/thrift/InvalidRequestException.java:1053690-1055654
-/cassandra/branches/cassandra-0.8/interface/thrift/gen-java/org/apache/cassandra/thrift/InvalidRequestException.java:1090934-1125013,1125019-1152110,1152265
+/cassandra/branches/cassandra-0.8/interface/thrift/gen-java/org/apache/cassandra/thrift/InvalidRequestException.java:1090934-1125013,1125019-1152110,1152265,1152793
 /cassandra/branches/cassandra-0.8.0/interface/thrift/gen-java/org/apache/cassandra/thrift/InvalidRequestException.java:1125021-1130369
 /cassandra/branches/cassandra-0.8.1/interface/thrift/gen-java/org/apache/cassandra/thrift/InvalidRequestException.java:1101014-1125018
 /cassandra/tags/cassandra-0.7.0-rc3/interface/thrift/gen-java/org/apache/cassandra/thrift/InvalidRequestException.java:1051699-1053689

Propchange: cassandra/trunk/interface/thrift/gen-java/org/apache/cassandra/thrift/NotFoundException.java
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Aug  1 14:31:36 2011
@@ -1,7 +1,7 @@
 /cassandra/branches/cassandra-0.6/interface/thrift/gen-java/org/apache/cassandra/thrift/NotFoundException.java:922689-1052356,1052358-1053452,1053454,1053456-1131291
 /cassandra/branches/cassandra-0.7/interface/thrift/gen-java/org/apache/cassandra/thrift/NotFoundException.java:1026516-1151306
 /cassandra/branches/cassandra-0.7.0/interface/thrift/gen-java/org/apache/cassandra/thrift/NotFoundException.java:1053690-1055654
-/cassandra/branches/cassandra-0.8/interface/thrift/gen-java/org/apache/cassandra/thrift/NotFoundException.java:1090934-1125013,1125019-1152110,1152265
+/cassandra/branches/cassandra-0.8/interface/thrift/gen-java/org/apache/cassandra/thrift/NotFoundException.java:1090934-1125013,1125019-1152110,1152265,1152793
 /cassandra/branches/cassandra-0.8.0/interface/thrift/gen-java/org/apache/cassandra/thrift/NotFoundException.java:1125021-1130369
 /cassandra/branches/cassandra-0.8.1/interface/thrift/gen-java/org/apache/cassandra/thrift/NotFoundException.java:1101014-1125018
 /cassandra/tags/cassandra-0.7.0-rc3/interface/thrift/gen-java/org/apache/cassandra/thrift/NotFoundException.java:1051699-1053689

Propchange: cassandra/trunk/interface/thrift/gen-java/org/apache/cassandra/thrift/SuperColumn.java
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Aug  1 14:31:36 2011
@@ -1,7 +1,7 @@
 /cassandra/branches/cassandra-0.6/interface/thrift/gen-java/org/apache/cassandra/thrift/SuperColumn.java:922689-1052356,1052358-1053452,1053454,1053456-1131291
 /cassandra/branches/cassandra-0.7/interface/thrift/gen-java/org/apache/cassandra/thrift/SuperColumn.java:1026516-1151306
 /cassandra/branches/cassandra-0.7.0/interface/thrift/gen-java/org/apache/cassandra/thrift/SuperColumn.java:1053690-1055654
-/cassandra/branches/cassandra-0.8/interface/thrift/gen-java/org/apache/cassandra/thrift/SuperColumn.java:1090934-1125013,1125019-1152110,1152265
+/cassandra/branches/cassandra-0.8/interface/thrift/gen-java/org/apache/cassandra/thrift/SuperColumn.java:1090934-1125013,1125019-1152110,1152265,1152793
 /cassandra/branches/cassandra-0.8.0/interface/thrift/gen-java/org/apache/cassandra/thrift/SuperColumn.java:1125021-1130369
 /cassandra/branches/cassandra-0.8.1/interface/thrift/gen-java/org/apache/cassandra/thrift/SuperColumn.java:1101014-1125018
 /cassandra/tags/cassandra-0.7.0-rc3/interface/thrift/gen-java/org/apache/cassandra/thrift/SuperColumn.java:1051699-1053689

Modified: cassandra/trunk/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/db/commitlog/CommitLog.java?rev=1152799&r1=1152798&r2=1152799&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/db/commitlog/CommitLog.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/db/commitlog/CommitLog.java Mon Aug  1 14:31:36
2011
@@ -395,6 +395,12 @@ public class CommitLog implements Commit
         }
     }
 
+    // for tests mainly
+    public int segmentsCount()
+    {
+        return segments.size();
+    }
+
     /*
      * Adds the specified row to the commit log. This method will reset the
      * file offset to what it is before the start of the operation in case
@@ -460,30 +466,36 @@ public class CommitLog implements Commit
             CommitLogSegment segment = iter.next();
             if (segment.id == context.segment)
             {
-                // we can't just mark the segment where the flush happened clean,
-                // since there may have been writes to it between when the flush
-                // started and when it finished.
-                segment.turnOn(id);
+                // Only unmark this segment if there were not write since the
+                // ReplayPosition was grabbed.
+                segment.turnOffIfNotWritten(id, context.position);
+                maybeDiscardSegment(segment, iter);
                 break;
             }
 
             segment.turnOff(id);
-            if (segment.isSafeToDelete() && iter.hasNext())
-            {
-                logger.info("Discarding obsolete commit log:" + segment);
-                segment.close();
-                DeletionService.executeDelete(segment.getPath());
-                // usually this will be the first (remaining) segment, but not always, if
segment A contains
-                // writes to a CF that is unflushed but is followed by segment B whose CFs
are all flushed.
-                iter.remove();
-            }
-            else
-            {
-                if (logger.isDebugEnabled())
-                    logger.debug("Not safe to delete commit log " + segment + "; dirty is
" + segment.dirtyString() + "; hasNext: " + iter.hasNext());
-            }
+            maybeDiscardSegment(segment, iter);
         }
     }
+
+    private void maybeDiscardSegment(CommitLogSegment segment, Iterator<CommitLogSegment>
iter)
+    {
+        if (segment.isSafeToDelete() && iter.hasNext())
+        {
+            logger.info("Discarding obsolete commit log:" + segment);
+            segment.close();
+            DeletionService.executeDelete(segment.getPath());
+            // usually this will be the first (remaining) segment, but not always, if segment
A contains
+            // writes to a CF that is unflushed but is followed by segment B whose CFs are
all flushed.
+            iter.remove();
+        }
+        else
+        {
+            if (logger.isDebugEnabled())
+                logger.debug("Not safe to delete commit log " + segment + "; dirty is " +
segment.dirtyString() + "; hasNext: " + iter.hasNext());
+        }
+    }
+
     
     void sync() throws IOException
     {
@@ -548,7 +560,7 @@ public class CommitLog implements Commit
                         // Force a flush on all CFs keeping the oldest segment from being
removed
                         CommitLogSegment oldestSegment = segments.peek();
                         assert oldestSegment != null; // has to be at least the one we just
added
-                        for (Integer dirtyCFId : oldestSegment.cfDirty)
+                        for (Integer dirtyCFId : oldestSegment.cfLastWrite.keySet())
                         {
                             String keypace = CFMetaData.getCF(dirtyCFId).left;
                             Table.open(keypace).getColumnFamilyStore(dirtyCFId).forceFlush();

Modified: cassandra/trunk/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java?rev=1152799&r1=1152798&r2=1152799&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java Mon Aug
 1 14:31:36 2011
@@ -23,8 +23,8 @@ package org.apache.cassandra.db.commitlo
 import java.io.File;
 import java.io.IOError;
 import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.zip.CRC32;
@@ -38,6 +38,7 @@ import org.slf4j.LoggerFactory;
 
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.db.RowMutation;
+import org.apache.cassandra.db.ColumnFamily;
 
 public class CommitLogSegment
 {
@@ -48,7 +49,7 @@ public class CommitLogSegment
     private final SequentialWriter logWriter;
 
     // cache which cf is dirty in this segment to avoid having to lookup all ReplayPositions
to decide if we could delete this segment
-    public final Set<Integer> cfDirty = new HashSet<Integer>();
+    public final Map<Integer, Integer> cfLastWrite = new HashMap<Integer, Integer>();
 
     public CommitLogSegment()
     {
@@ -102,6 +103,21 @@ public class CommitLogSegment
             assert currentPosition <= Integer.MAX_VALUE;
             ReplayPosition cLogCtx = new ReplayPosition(id, (int) currentPosition);
 
+            for (ColumnFamily columnFamily : rowMutation.getColumnFamilies())
+            {
+                // check for null cfm in case a cl write goes through after the cf is
+                // defined but before a new segment is created.
+                CFMetaData cfm = DatabaseDescriptor.getCFMetaData(columnFamily.id());
+                if (cfm == null)
+                {
+                    logger.error("Attempted to write commit log entry for unrecognized column
family: " + columnFamily.id());
+                }
+                else
+                {
+                    turnOn(cfm.cfId, (int) currentPosition);
+                }
+            }
+
             // write mutation, w/ checksum on the size and data
             Checksum checksum = new CRC32();
             byte[] serializedRow = rowMutation.getSerializedBuffer(MessagingService.version_);
@@ -168,21 +184,32 @@ public class CommitLogSegment
         }
     }
 
-    void turnOn(Integer cfId)
+    void turnOn(Integer cfId, Integer position)
+    {
+        cfLastWrite.put(cfId, position);
+    }
+
+    /**
+     * Turn the dirty bit off only if there has been no write since the flush
+     * position was grabbed.
+     */
+    void turnOffIfNotWritten(Integer cfId, Integer flushPosition)
     {
-        cfDirty.add(cfId);
+        Integer lastWritten = cfLastWrite.get(cfId);
+        if (lastWritten == null || lastWritten < flushPosition)
+            cfLastWrite.remove(cfId);
     }
 
     void turnOff(Integer cfId)
     {
-        cfDirty.remove(cfId);
+        cfLastWrite.remove(cfId);
     }
 
     // For debugging, not fast
     String dirtyString()
     {
         StringBuilder sb = new StringBuilder();
-        for (Integer cfId : cfDirty)
+        for (Integer cfId : cfLastWrite.keySet())
         {
             CFMetaData m = DatabaseDescriptor.getCFMetaData(cfId);
             sb.append(m == null ? m.cfName : "<deleted>").append(" (").append(cfId).append("),
");
@@ -192,7 +219,7 @@ public class CommitLogSegment
 
     boolean isSafeToDelete()
     {
-        return cfDirty.isEmpty();
+        return cfLastWrite.isEmpty();
     }
 
     @Override

Modified: cassandra/trunk/test/unit/org/apache/cassandra/db/CommitLogTest.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/test/unit/org/apache/cassandra/db/CommitLogTest.java?rev=1152799&r1=1152798&r2=1152799&view=diff
==============================================================================
--- cassandra/trunk/test/unit/org/apache/cassandra/db/CommitLogTest.java (original)
+++ cassandra/trunk/test/unit/org/apache/cassandra/db/CommitLogTest.java Mon Aug  1 14:31:36
2011
@@ -20,6 +20,7 @@
 package org.apache.cassandra.db;
 
 import java.io.*;
+import java.nio.ByteBuffer;
 import java.util.zip.CRC32;
 import java.util.zip.Checksum;
 
@@ -29,6 +30,7 @@ import org.apache.cassandra.CleanupHelpe
 import org.apache.cassandra.db.commitlog.CommitLog;
 import org.apache.cassandra.db.filter.QueryPath;
 import org.apache.cassandra.utils.Pair;
+import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
 
 public class CommitLogTest extends CleanupHelper
 {
@@ -87,6 +89,74 @@ public class CommitLogTest extends Clean
         testRecoveryWithBadSizeArgument(-10, 10); // negative size, but no EOF
     }
 
+    @Test
+    public void testDontDeleteIfDirty() throws Exception
+    {
+        CommitLog.instance.resetUnsafe();
+        // Roughly 32 MB mutation
+        RowMutation rm = new RowMutation("Keyspace1", bytes("k"));
+        rm.add(new QueryPath("Standard1", null, bytes("c1")), ByteBuffer.allocate(32 * 1024
* 1024), 0);
+
+        // Adding it 5 times
+        CommitLog.instance.add(rm);
+        CommitLog.instance.add(rm);
+        CommitLog.instance.add(rm);
+        CommitLog.instance.add(rm);
+        CommitLog.instance.add(rm);
+
+        // Adding new mutation on another CF
+        RowMutation rm2 = new RowMutation("Keyspace1", bytes("k"));
+        rm2.add(new QueryPath("Standard2", null, bytes("c1")), ByteBuffer.allocate(4), 0);
+        CommitLog.instance.add(rm2);
+
+        assert CommitLog.instance.segmentsCount() == 2 : "Expecting 2 segments, got " + CommitLog.instance.segmentsCount();
+
+        int cfid2 = rm2.getColumnFamilyIds().iterator().next();
+        CommitLog.instance.discardCompletedSegments(cfid2, CommitLog.instance.getContext());
+
+        // Assert we still have both our segment
+        assert CommitLog.instance.segmentsCount() == 2 : "Expecting 2 segments, got " + CommitLog.instance.segmentsCount();
+    }
+
+    @Test
+    public void testDeleteIfNotDirty() throws Exception
+    {
+        CommitLog.instance.resetUnsafe();
+        // Roughly 32 MB mutation
+        RowMutation rm = new RowMutation("Keyspace1", bytes("k"));
+        rm.add(new QueryPath("Standard1", null, bytes("c1")), ByteBuffer.allocate(32 * 1024
* 1024), 0);
+
+        // Adding it twice (won't change segment)
+        CommitLog.instance.add(rm);
+        CommitLog.instance.add(rm);
+
+        assert CommitLog.instance.segmentsCount() == 1 : "Expecting 1 segment, got " + CommitLog.instance.segmentsCount();
+
+        // "Flush": this won't delete anything
+        int cfid1 = rm.getColumnFamilyIds().iterator().next();
+        CommitLog.instance.discardCompletedSegments(cfid1, CommitLog.instance.getContext());
+
+        assert CommitLog.instance.segmentsCount() == 1 : "Expecting 1 segment, got " + CommitLog.instance.segmentsCount();
+
+        // Adding new mutation on another CF so that a new segment is created
+        RowMutation rm2 = new RowMutation("Keyspace1", bytes("k"));
+        rm2.add(new QueryPath("Standard2", null, bytes("c1")), ByteBuffer.allocate(64 * 1024
* 1024), 0);
+        CommitLog.instance.add(rm2);
+        CommitLog.instance.add(rm2);
+
+        assert CommitLog.instance.segmentsCount() == 2 : "Expecting 2 segments, got " + CommitLog.instance.segmentsCount();
+
+
+        // "Flush" second cf: The first segment should be deleted since we
+        // didn't write anything on cf1 since last flush (and we flush cf2)
+
+        int cfid2 = rm2.getColumnFamilyIds().iterator().next();
+        CommitLog.instance.discardCompletedSegments(cfid2, CommitLog.instance.getContext());
+
+        // Assert we still have both our segment
+        assert CommitLog.instance.segmentsCount() == 1 : "Expecting 1 segment, got " + CommitLog.instance.segmentsCount();
+    }
+
     protected void testRecoveryWithBadSizeArgument(int size, int dataSize) throws Exception
     {
         Checksum checksum = new CRC32();



Mime
View raw message