diff --git a/CHANGES.txt b/CHANGES.txt index c5afdc9ed9..ecb7592df7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -33,6 +33,8 @@ 0.8.4 * include files-to-be-streamed in StreamInSession.getSources (CASSANDRA-2972) + * use JAVA env var in cassandra-env.sh (CASSANDRA-2785, 2992) + * avoid doing read for no-op replicate-on-write at CL=1 (CASSANDRA-2892) 0.8.3 diff --git a/debian/control b/debian/control index 01cf8e7d49..38cbd84863 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: cassandra Section: misc Priority: extra Maintainer: Eric Evans -Build-Depends: debhelper (>= 5), openjdk-6-jdk (>= 6b11) | java6-sdk, ant (>= 1.7), ant-optional (>= 1.7) +Build-Depends: debhelper (>= 5), openjdk-6-jdk (>= 6b11) | java6-sdk, ant (>= 1.7), ant-optional (>= 1.7), subversion Homepage: http://cassandra.apache.org Vcs-Svn: https://svn.apache.org/repos/asf/cassandra/trunk Vcs-Browser: http://svn.apache.org/viewvc/cassandra/trunk diff --git a/redhat/cassandra b/redhat/cassandra index 83d5e0deea..3b7f0483b6 100644 --- a/redhat/cassandra +++ b/redhat/cassandra @@ -9,7 +9,6 @@ . /etc/rc.d/init.d/functions -export JAVA_HOME=/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0/ export CASSANDRA_HOME=/usr/share/cassandra/ export CASSANDRA_INCLUDE=/usr/share/cassandra/cassandra.in.sh export CASSANDRA_CONF=/etc/cassandra/conf @@ -19,9 +18,36 @@ log_file=/var/log/cassandra/cassandra.log pid_file=/var/run/cassandra/cassandra.pid CASSANDRA_PROG=/usr/sbin/cassandra +# The first existing directory is used for JAVA_HOME if needed. +JVM_SEARCH_DIRS="/usr/lib/jvm/jre /usr/lib/jvm/jre-1.6.* /usr/lib/jvm/java-1.6.*/jre" + # Read configuration variable file if it is present [ -r /etc/default/$NAME ] && . /etc/default/$NAME +# If JAVA_HOME has not been set, try to determine it. +if [ -z "$JAVA_HOME" ]; then + # If java is in PATH, use a JAVA_HOME that corresponds to that. This is + # both consistent with how the upstream startup script works, and with + # the use of alternatives to set a system JVM (as is done on Debian and + # Red Hat derivatives). + java="`/usr/bin/which java 2>/dev/null`" + if [ -n "$java" ]; then + java=`readlink --canonicalize "$java"` + JAVA_HOME=`dirname "\`dirname \$java\`"` + else + # No JAVA_HOME set and no java found in PATH; search for a JVM. + for jdir in $JVM_SEARCH_DIRS; do + if [ -x "$jdir/bin/java" ]; then + JAVA_HOME="$jdir" + break + fi + done + # if JAVA_HOME is still empty here, punt. + fi +fi +JAVA="$JAVA_HOME/bin/java" +export JAVA_HOME JAVA + case "$1" in start) # Cassandra startup diff --git a/src/java/org/apache/cassandra/cli/Cli.g b/src/java/org/apache/cassandra/cli/Cli.g index 66f199f4c7..9ab556d972 100644 --- a/src/java/org/apache/cassandra/cli/Cli.g +++ b/src/java/org/apache/cassandra/cli/Cli.g @@ -44,6 +44,7 @@ tokens { NODE_SHOW_CLUSTER_NAME; NODE_SHOW_VERSION; NODE_SHOW_KEYSPACES; + NODE_SHOW_SCHEMA; NODE_THRIFT_GET; NODE_THRIFT_GET_WITH_CONDITIONS; NODE_THRIFT_SET; @@ -191,6 +192,8 @@ helpStatement -> ^(NODE_HELP NODE_SHOW_CLUSTER_NAME) | HELP SHOW KEYSPACES -> ^(NODE_HELP NODE_SHOW_KEYSPACES) + | HELP SHOW SCHEMA + -> ^(NODE_HELP NODE_SHOW_SCHEMA) | HELP SHOW API_VERSION -> ^(NODE_HELP NODE_SHOW_VERSION) | HELP CREATE KEYSPACE @@ -284,6 +287,7 @@ showStatement : showClusterName | showVersion | showKeyspaces + | showSchema ; listStatement @@ -356,6 +360,11 @@ showKeyspaces -> ^(NODE_SHOW_KEYSPACES) ; +showSchema + : SHOW SCHEMA (keyspace)? + -> ^(NODE_SHOW_SCHEMA (keyspace)?) + ; + describeTable : DESCRIBE KEYSPACE (keyspace)? -> ^(NODE_DESCRIBE_TABLE (keyspace)?) @@ -573,6 +582,7 @@ TTL: 'TTL'; CONSISTENCYLEVEL: 'CONSISTENCYLEVEL'; INDEX: 'INDEX'; ON: 'ON'; +SCHEMA: 'SCHEMA'; IP_ADDRESS : IntegerPositiveLiteral '.' IntegerPositiveLiteral '.' IntegerPositiveLiteral '.' IntegerPositiveLiteral diff --git a/src/java/org/apache/cassandra/cli/CliClient.java b/src/java/org/apache/cassandra/cli/CliClient.java index 873e80ad16..4371d91759 100644 --- a/src/java/org/apache/cassandra/cli/CliClient.java +++ b/src/java/org/apache/cassandra/cli/CliClient.java @@ -26,6 +26,9 @@ import java.nio.ByteBuffer; import java.nio.charset.CharacterCodingException; import java.util.*; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import org.apache.commons.lang.StringUtils; import com.google.common.base.Charsets; import com.google.common.base.Joiner; @@ -71,7 +74,7 @@ public class CliClient COUNTERCOLUMN (CounterColumnType.instance); private AbstractType validator; - + Function(AbstractType validator) { this.validator = validator; @@ -139,6 +142,8 @@ public class CliClient } private static final String DEFAULT_PLACEMENT_STRATEGY = "org.apache.cassandra.locator.NetworkTopologyStrategy"; + private final String NEWLINE = System.getProperty("line.separator"); + private final String TAB = " "; private Cassandra.Client thriftClient = null; private CliSessionState sessionState = null; @@ -244,6 +249,9 @@ public class CliClient case CliParser.NODE_SHOW_KEYSPACES: executeShowKeySpaces(); break; + case CliParser.NODE_SHOW_SCHEMA: + executeShowSchema(tree); + break; case CliParser.NODE_DESCRIBE_TABLE: executeDescribeKeySpace(tree); break; @@ -1249,7 +1257,7 @@ public class CliClient cfDef.setReplicate_on_write(Boolean.parseBoolean(mValue)); break; case ROW_CACHE_PROVIDER: - cfDef.setRow_cache_provider(mValue); + cfDef.setRow_cache_provider(CliUtils.unescapeSQLString(mValue)); break; case KEY_VALIDATION_CLASS: cfDef.setKey_validation_class(CliUtils.unescapeSQLString(mValue)); @@ -1572,6 +1580,187 @@ public class CliClient } } + // SHOW SCHEMA + private void executeShowSchema(Tree statement) throws TException, InvalidRequestException + { + if (!CliMain.isConnected()) + return; + + final List keyspaces = thriftClient.describe_keyspaces(); + Collections.sort(keyspaces, new KsDefNamesComparator()); + final String keyspaceName = (statement.getChildCount() == 0) + ? keySpace + : CliCompiler.getKeySpace(statement, keyspaces); + + Iterator ksIter; + if (keyspaceName != null) + ksIter = Collections2.filter(keyspaces, new Predicate() + { + public boolean apply(KsDef ksDef) + { + return keyspaceName.equals(ksDef.name); + } + }).iterator(); + else + ksIter = keyspaces.iterator(); + + + final StringBuilder sb = new StringBuilder(); + while (ksIter.hasNext()) + showKeyspace(sb, ksIter.next()); + + sessionState.out.printf(sb.toString()); + } + + /** + * Creates a CLI script to create the Keyspace it's Column Families + * @param sb StringBuilder to write to. + * @param ksDef KsDef to create the cli script for. + */ + private void showKeyspace(StringBuilder sb, KsDef ksDef) + { + + sb.append("create keyspace " + ksDef.name); + if (ksDef.isSetReplication_factor()) + writeAttr(sb, false, "replication_factor", ksDef.getReplication_factor()); + writeAttr(sb, true, "placement_strategy", normaliseType(ksDef.strategy_class, "org.apache.cassandra.locator")); + if (ksDef.strategy_options != null && !ksDef.strategy_options.isEmpty()) + { + final StringBuilder opts = new StringBuilder(); + opts.append("[{"); + String prefix = ""; + for (Map.Entry opt : ksDef.strategy_options.entrySet()) + { + opts.append(prefix + CliUtils.escapeSQLString(opt.getKey()) + " : " + CliUtils.escapeSQLString(opt.getValue())); + prefix = ", "; + } + opts.append("}]"); + writeAttrRaw(sb, false, "strategy_options", opts.toString()); + } + sb.append(";" + NEWLINE); + sb.append(NEWLINE); + + sb.append("use " + ksDef.name + ";"); + sb.append(NEWLINE); + sb.append(NEWLINE); + + Collections.sort(ksDef.cf_defs, new CfDefNamesComparator()); + for (CfDef cfDef : ksDef.cf_defs) + showColumnFamily(sb, cfDef); + sb.append(NEWLINE); + sb.append(NEWLINE); + } + + /** + * Creates a CLI script for the CfDef including meta data to the supplied StringBuilder. + * @param sb + * @param cfDef + */ + private void showColumnFamily(StringBuilder sb, CfDef cfDef) + { + sb.append("create column family " + CliUtils.escapeSQLString(cfDef.name)); + + writeAttr(sb, true, "column_type", cfDef.column_type); + writeAttr(sb, false, "comparator", normaliseType(cfDef.comparator_type, "org.apache.cassandra.db.marshal")); + if (cfDef.column_type == "Super") + writeAttr(sb, false, "subcomparator", normaliseType(cfDef.subcomparator_type, "org.apache.cassandra.db.marshal")); + if (!StringUtils.isEmpty(cfDef.default_validation_class)) + writeAttr(sb, false, "default_validation_class", + normaliseType(cfDef.default_validation_class, "org.apache.cassandra.db.marshal")); + writeAttr(sb, false, "key_validation_class", + normaliseType(cfDef.key_validation_class, "org.apache.cassandra.db.marshal")); + writeAttr(sb, false, "memtable_operations", cfDef.memtable_operations_in_millions); + writeAttr(sb, false, "memtable_throughput", cfDef.memtable_throughput_in_mb); + writeAttr(sb, false, "memtable_flush_after", cfDef.memtable_flush_after_mins); + writeAttr(sb, false, "rows_cached", cfDef.row_cache_size); + writeAttr(sb, false, "row_cache_save_period", cfDef.row_cache_save_period_in_seconds); + writeAttr(sb, false, "keys_cached", cfDef.key_cache_size); + writeAttr(sb, false, "key_cache_save_period", cfDef.key_cache_save_period_in_seconds); + writeAttr(sb, false, "read_repair_chance", cfDef.read_repair_chance); + writeAttr(sb, false, "gc_grace", cfDef.gc_grace_seconds); + writeAttr(sb, false, "min_compaction_threshold", cfDef.min_compaction_threshold); + writeAttr(sb, false, "max_compaction_threshold", cfDef.max_compaction_threshold); + writeAttr(sb, false, "replicate_on_write", cfDef.replicate_on_write); + writeAttr(sb, false, "row_cache_provider", normaliseType(cfDef.row_cache_provider, "org.apache.cassandra.cache")); + + if (!StringUtils.isEmpty(cfDef.comment)) + writeAttr(sb, false, "comment", cfDef.comment); + + if (!cfDef.column_metadata.isEmpty()) + { + StringBuilder colSb = new StringBuilder(); + colSb.append("["); + boolean first = true; + for (ColumnDef colDef : cfDef.column_metadata) + { + if (!first) + colSb.append(","); + first = false; + showColumnMeta(colSb, cfDef, colDef); + } + colSb.append("]"); + writeAttrRaw(sb, false, "column_metadata", colSb.toString()); + } + sb.append(";"); + sb.append(NEWLINE); + sb.append(NEWLINE); + } + + /** + * Writes the supplied ColumnDef to the StringBuilder as a cli script. + * @param sb + * @param cfDef + * @param colDef + */ + private void showColumnMeta(StringBuilder sb, CfDef cfDef, ColumnDef colDef) + { + sb.append(NEWLINE + TAB + TAB + "{"); + + final AbstractType comparator = getFormatType((cfDef.column_type == "Super") + ? cfDef.subcomparator_type + : cfDef.comparator_type); + sb.append("column_name : '" + CliUtils.escapeSQLString(comparator.getString(colDef.name)) + "'," + NEWLINE); + String validationClass = normaliseType(colDef.validation_class, "org.apache.cassandra.db.marshal"); + sb.append(TAB + TAB + "validation_class : " + CliUtils.escapeSQLString(validationClass)); + if (colDef.isSetIndex_name()) + { + sb.append("," + NEWLINE); + sb.append(TAB + TAB + "index_name : '" + CliUtils.escapeSQLString(colDef.index_name) + "'," + NEWLINE); + sb.append(TAB + TAB + "index_type : " + CliUtils.escapeSQLString(Integer.toString(colDef.index_type.getValue()))); + + } + sb.append("}"); + } + + private String normaliseType(String path, String expectedPackage) + { + if (path.startsWith(expectedPackage)) + return path.substring(expectedPackage.length() + 1); + + return path; + } + + private void writeAttr(StringBuilder sb, boolean first, String name, Boolean value) + { + writeAttrRaw(sb, first, name, value.toString()); + } + private void writeAttr(StringBuilder sb, boolean first, String name, Number value) + { + writeAttrRaw(sb, first, name, value.toString()); + } + + private void writeAttr(StringBuilder sb, boolean first, String name, String value) + { + writeAttrRaw(sb, first, name, "'" + CliUtils.escapeSQLString(value) + "'"); + } + + private void writeAttrRaw(StringBuilder sb, boolean first, String name, String value) + { + sb.append(NEWLINE + TAB); + sb.append(first ? "with " : "and "); + sb.append(name + " = "); + sb.append(value); + } /** * Returns true if this.keySpace is set, false otherwise * @return boolean diff --git a/src/java/org/apache/cassandra/cli/CliCompleter.java b/src/java/org/apache/cassandra/cli/CliCompleter.java index 19a082005d..6f4e583a36 100644 --- a/src/java/org/apache/cassandra/cli/CliCompleter.java +++ b/src/java/org/apache/cassandra/cli/CliCompleter.java @@ -29,6 +29,7 @@ public class CliCompleter extends SimpleCompletor "quit", "show cluster name", "show keyspaces", + "show schema", "show api version", "create keyspace", "create column family", @@ -45,6 +46,7 @@ public class CliCompleter extends SimpleCompletor "help quit", "help show cluster name", "help show keyspaces", + "help show schema", "help show api version", "help create keyspace", "help create column family", diff --git a/src/java/org/apache/cassandra/service/StorageProxy.java b/src/java/org/apache/cassandra/service/StorageProxy.java index 5d144df576..e71902fbd3 100644 --- a/src/java/org/apache/cassandra/service/StorageProxy.java +++ b/src/java/org/apache/cassandra/service/StorageProxy.java @@ -96,7 +96,7 @@ public class StorageProxy implements StorageProxyMBean public void apply(IMutation mutation, Multimap hintedEndpoints, IWriteResponseHandler responseHandler, String localDataCenter, ConsistencyLevel consistency_level) throws IOException { assert mutation instanceof RowMutation; - sendToHintedEndpoints((RowMutation) mutation, hintedEndpoints, responseHandler, localDataCenter, true, consistency_level); + sendToHintedEndpoints((RowMutation) mutation, hintedEndpoints, responseHandler, localDataCenter, consistency_level); } }; @@ -110,7 +110,11 @@ public class StorageProxy implements StorageProxyMBean { public void apply(IMutation mutation, Multimap hintedEndpoints, IWriteResponseHandler responseHandler, String localDataCenter, ConsistencyLevel consistency_level) throws IOException { - applyCounterMutation(mutation, hintedEndpoints, responseHandler, localDataCenter, consistency_level, false); + if (logger.isDebugEnabled()) + logger.debug("insert writing local & replicate " + mutation.toString(true)); + + Runnable runnable = counterWriteTask(mutation, hintedEndpoints, responseHandler, localDataCenter, consistency_level); + runnable.run(); } }; @@ -118,7 +122,11 @@ public class StorageProxy implements StorageProxyMBean { public void apply(IMutation mutation, Multimap hintedEndpoints, IWriteResponseHandler responseHandler, String localDataCenter, ConsistencyLevel consistency_level) throws IOException { - applyCounterMutation(mutation, hintedEndpoints, responseHandler, localDataCenter, consistency_level, true); + if (logger.isDebugEnabled()) + logger.debug("insert writing local & replicate " + mutation.toString(true)); + + Runnable runnable = counterWriteTask(mutation, hintedEndpoints, responseHandler, localDataCenter, consistency_level); + StageManager.getStage(Stage.MUTATION).execute(runnable); } }; } @@ -218,7 +226,7 @@ public class StorageProxy implements StorageProxyMBean return ss.getTokenMetadata().getWriteEndpoints(StorageService.getPartitioner().getToken(key), table, naturalEndpoints); } - private static void sendToHintedEndpoints(final RowMutation rm, Multimap hintedEndpoints, IWriteResponseHandler responseHandler, String localDataCenter, boolean insertLocalMessages, ConsistencyLevel consistency_level) + private static void sendToHintedEndpoints(final RowMutation rm, Multimap hintedEndpoints, IWriteResponseHandler responseHandler, String localDataCenter, ConsistencyLevel consistency_level) throws IOException { // Multimap that holds onto all the messages and addresses meant for a specific datacenter @@ -237,8 +245,7 @@ public class StorageProxy implements StorageProxyMBean // unhinted writes if (destination.equals(FBUtilities.getBroadcastAddress())) { - if (insertLocalMessages) - insertLocal(rm, responseHandler); + insertLocal(rm, responseHandler); } else { @@ -425,13 +432,9 @@ public class StorageProxy implements StorageProxyMBean return performWrite(cm, cm.consistency(), localDataCenter, counterWriteOnCoordinatorPerformer); } - private static void applyCounterMutation(final IMutation mutation, final Multimap hintedEndpoints, final IWriteResponseHandler responseHandler, final String localDataCenter, final ConsistencyLevel consistency_level, boolean executeOnMutationStage) + private static Runnable counterWriteTask(final IMutation mutation, final Multimap hintedEndpoints, final IWriteResponseHandler responseHandler, final String localDataCenter, final ConsistencyLevel consistency_level) { - // we apply locally first, then send it to other replica - if (logger.isDebugEnabled()) - logger.debug("insert writing local & replicate " + mutation.toString(true)); - - Runnable runnable = new DroppableRunnable(StorageService.Verb.MUTATION) + return new DroppableRunnable(StorageService.Verb.MUTATION) { public void runMayThrow() throws IOException { @@ -440,10 +443,11 @@ public class StorageProxy implements StorageProxyMBean // apply mutation cm.apply(); - responseHandler.response(null); - if (cm.shouldReplicateOnWrite()) + // then send to replicas, if any + hintedEndpoints.removeAll(FBUtilities.getLocalAddress()); + if (cm.shouldReplicateOnWrite() && !hintedEndpoints.isEmpty()) { // We do the replication on another stage because it involves a read (see CM.makeReplicationMutation) // and we want to avoid blocking too much the MUTATION stage @@ -452,16 +456,12 @@ public class StorageProxy implements StorageProxyMBean public void runMayThrow() throws IOException { // send mutation to other replica - sendToHintedEndpoints(cm.makeReplicationMutation(), hintedEndpoints, responseHandler, localDataCenter, false, consistency_level); + sendToHintedEndpoints(cm.makeReplicationMutation(), hintedEndpoints, responseHandler, localDataCenter, consistency_level); } }); } } }; - if (executeOnMutationStage) - StageManager.getStage(Stage.MUTATION).execute(runnable); - else - runnable.run(); } /** diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java index 90e68d6141..0b638b4f23 100644 --- a/src/java/org/apache/cassandra/service/StorageService.java +++ b/src/java/org/apache/cassandra/service/StorageService.java @@ -596,7 +596,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe for (InetAddress endpoint: entry.getValue()) { if (endpoint.equals(FBUtilities.getLocalAddress())) - rpcaddrs.add(DatabaseDescriptor.getRpcAddress().toString()); + rpcaddrs.add(DatabaseDescriptor.getRpcAddress().getHostAddress()); else rpcaddrs.add(Gossiper.instance.getEndpointStateForEndpoint(endpoint).getApplicationState(ApplicationState.RPC_ADDRESS).value); } diff --git a/src/resources/org/apache/cassandra/cli/CliHelp.yaml b/src/resources/org/apache/cassandra/cli/CliHelp.yaml index c7ed23b0d8..dc4793beb8 100644 --- a/src/resources/org/apache/cassandra/cli/CliHelp.yaml +++ b/src/resources/org/apache/cassandra/cli/CliHelp.yaml @@ -52,6 +52,7 @@ help: | show api version Show the server API version. show cluster name Show the cluster name. show keyspaces Show all keyspaces and their column families. + show schema Show a cli script to create keyspaces and column families. truncate Drop the data in a column family. update column family Update the settings for a column family. update keyspace Update the settings for a keyspace. @@ -116,7 +117,8 @@ commands: use Keyspace1 user 'badpasswd'; - name: NODE_DESCRIBE_TABLE help: | - describe keyspace ()?; + describe keyspace; + describe keyspace ; Describes the settings for the current or named keyspace, and the settings for all column families in the keyspace. @@ -126,7 +128,7 @@ commands: Examples: describe keyspace; - describe keyspace system; + describe keyspace Keyspace1; - name: NODE_DESCRIBE_CLUSTER help: | describe cluster; @@ -177,6 +179,22 @@ commands: Examples: show keyspaces; + - name: NODE_SHOW_SCHEMA + help: | + show schema; + show schema ; + + Creates a CLI script to create the current, specified or all keyspaces + and their column families. + + Optional Parameters: + - keyspace: Name of the keyspace to create the script for. If omitted + the current keyspace is used, if there is no current keyspace all + keyspaces are considered. + + Examples: + show schema; + show schema Keyspace1; - name: NODE_ADD_KEYSPACE help: | create keyspace ; diff --git a/test/unit/org/apache/cassandra/cli/CliTest.java b/test/unit/org/apache/cassandra/cli/CliTest.java index d630e0fbe9..6cd2f67f3a 100644 --- a/test/unit/org/apache/cassandra/cli/CliTest.java +++ b/test/unit/org/apache/cassandra/cli/CliTest.java @@ -168,6 +168,7 @@ public class CliTest extends CleanupHelper "help QUIT", "help show cluster name", "help show keyspaces", + "help show schema", "help show api version", "help create keyspace", "HELP update KEYSPACE", @@ -183,7 +184,9 @@ public class CliTest extends CleanupHelper "HELP TRUNCATE", "help assume", "HELP", - "?" + "?", + "show schema", + "show schema TestKeySpace" }; @Test