You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@drill.apache.org by pa...@apache.org on 2018/01/09 01:29:45 UTC

[2/5] drill git commit: DRILL-5068: Create sys.profiles table

DRILL-5068: Create sys.profiles table

(Thanks to Hongze Zhang for the initial work)
Introduced a non-distributed (i.e. the foreman Drillbit executes a single fragment query) system table to list the completed profiles that are visible to it in the persistent store. This allows for querying the profiles on the following common metrics:
    queryId
    startTime
    foreman
    fragments
    user
    queue
    planTime
    queueTime
    executeTime
    totalTime
    state
    query

An additional system table - `sys.profiles_json` has also been introduced. This only has the following fields:
    queryId
    json
This allows for more detail metrics to be queried on the profile as a JSON document. Users are expected to use `sys.profiles` to identify profiles on the available metrics (such as long executeTime or fragments), and use the queryId to find the corresponding JSON document representation for further drill down into the profiles.
Updated TestMetadataProvider with new tally for current table and column counting tests

Security check to prevent unauthorized access to profile listing

Updated based on review comments

This closes #1077


Project: http://git-wip-us.apache.org/repos/asf/drill/repo
Commit: http://git-wip-us.apache.org/repos/asf/drill/commit/13a568a5
Tree: http://git-wip-us.apache.org/repos/asf/drill/tree/13a568a5
Diff: http://git-wip-us.apache.org/repos/asf/drill/diff/13a568a5

Branch: refs/heads/master
Commit: 13a568a53026df66a0b62e7ce230a2fcfd6731bd
Parents: ffd7d7e
Author: Kunal Khatua <kk...@maprtech.com>
Authored: Thu Dec 28 13:18:58 2017 -0800
Committer: Parth Chandra <pa...@apache.org>
Committed: Mon Jan 8 17:27:30 2018 -0800

----------------------------------------------------------------------
 .../apache/drill/exec/ops/FragmentContext.java  |  10 ++
 .../exec/server/QueryProfileStoreContext.java   |   2 -
 .../exec/store/sys/ProfileInfoIterator.java     | 160 +++++++++++++++++++
 .../drill/exec/store/sys/ProfileIterator.java   | 154 ++++++------------
 .../exec/store/sys/ProfileJsonIterator.java     | 139 ++++++++++++++++
 .../drill/exec/store/sys/SystemTable.java       |  19 ++-
 .../store/sys/store/LocalPersistentStore.java   |   2 +
 .../drill/exec/store/sys/TestSystemTable.java   |   5 +
 .../work/metadata/TestMetadataProvider.java     |   8 +-
 9 files changed, 384 insertions(+), 115 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/drill/blob/13a568a5/exec/java-exec/src/main/java/org/apache/drill/exec/ops/FragmentContext.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/ops/FragmentContext.java b/exec/java-exec/src/main/java/org/apache/drill/exec/ops/FragmentContext.java
index 405d37a..210d0d4 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/ops/FragmentContext.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/ops/FragmentContext.java
@@ -399,6 +399,16 @@ public class FragmentContext extends BaseFragmentContext implements AutoCloseabl
     return getConfig().getBoolean(ExecConstants.IMPERSONATION_ENABLED);
   }
 
+  public boolean isUserAuthenticationEnabled() {
+    // TODO(DRILL-2097): Until SimpleRootExec tests are removed, we need to consider impersonation disabled if there is
+    // no config
+    if (getConfig() == null) {
+      return false;
+    }
+
+    return getConfig().getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED);
+  }
+
   @Override
   public void close() {
     waitForSendComplete();

http://git-wip-us.apache.org/repos/asf/drill/blob/13a568a5/exec/java-exec/src/main/java/org/apache/drill/exec/server/QueryProfileStoreContext.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/QueryProfileStoreContext.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/QueryProfileStoreContext.java
index 7f282d5..dc2548e 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/QueryProfileStoreContext.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/QueryProfileStoreContext.java
@@ -19,7 +19,6 @@ package org.apache.drill.exec.server;
 
 import org.apache.drill.common.config.DrillConfig;
 import org.apache.drill.common.exceptions.DrillRuntimeException;
-import org.apache.drill.exec.ExecConstants;
 import org.apache.drill.exec.coord.ClusterCoordinator;
 import org.apache.drill.exec.coord.store.TransientStore;
 import org.apache.drill.exec.coord.store.TransientStoreConfig;
@@ -30,7 +29,6 @@ import org.apache.drill.exec.proto.UserBitShared.QueryProfile;
 import org.apache.drill.exec.store.sys.PersistentStore;
 import org.apache.drill.exec.store.sys.PersistentStoreConfig;
 import org.apache.drill.exec.store.sys.PersistentStoreProvider;
-import org.apache.drill.exec.store.sys.PersistentStoreConfig.StoreConfigBuilder;
 
 public class QueryProfileStoreContext {
   private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(QueryProfileStoreContext.class);

http://git-wip-us.apache.org/repos/asf/drill/blob/13a568a5/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/ProfileInfoIterator.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/ProfileInfoIterator.java b/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/ProfileInfoIterator.java
new file mode 100644
index 0000000..e9282c3
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/ProfileInfoIterator.java
@@ -0,0 +1,160 @@
+/**
+ * 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.drill.exec.store.sys;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterators;
+
+import org.apache.drill.exec.ops.FragmentContext;
+import org.apache.drill.exec.proto.UserBitShared;
+import org.apache.drill.exec.proto.UserBitShared.QueryProfile;
+
+import javax.annotation.Nullable;
+import java.sql.Timestamp;
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+/**
+ * System table listing completed profiles
+ */
+public class ProfileInfoIterator extends ProfileIterator {
+  static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ProfileInfoIterator.class);
+
+  private final Iterator<ProfileInfo> itr;
+
+  public ProfileInfoIterator(FragmentContext context) {
+    super(context);
+    itr = iterateProfileInfo();
+  }
+
+  //Returns an iterator for authorized profiles
+  private Iterator<ProfileInfo> iterateProfileInfo() {
+    try {
+      //Transform authorized profiles to iterator for ProfileInfo
+      return transform(
+          getAuthorizedProfiles(
+            profileStoreContext
+              .getCompletedProfileStore()
+              .getAll(),
+            queryingUsername, isAdmin));
+
+    } catch (Exception e) {
+      logger.error(e.getMessage());
+      return Iterators.singletonIterator(ProfileInfo.getDefault());
+    }
+  }
+
+  /**
+   * Iterating persistentStore as a iterator of {@link org.apache.drill.exec.store.sys.ProfileInfoIterator.ProfileInfo}.
+   */
+  private Iterator<ProfileInfo> transform(Iterator<Entry<String, UserBitShared.QueryProfile>> all) {
+    return Iterators.transform(all, new Function<Entry<String, UserBitShared.QueryProfile>, ProfileInfo>() {
+      @Nullable
+      @Override
+      public ProfileInfo apply(@Nullable Entry<String, UserBitShared.QueryProfile> input) {
+        if (input == null || input.getValue() == null) {
+          return ProfileInfo.getDefault();
+        }
+
+        //Constructing ProfileInfo
+        final String queryID = input.getKey();
+        final QueryProfile profile = input.getValue();
+        //For cases where query was never queued
+        final long assumedQueueEndTime = profile.getQueueWaitEnd()> 0 ? profile.getQueueWaitEnd() : profile.getPlanEnd();
+        return new ProfileInfo(
+            queryID,
+            new Timestamp(profile.getStart()),
+            profile.getForeman().getAddress(),
+            profile.getTotalFragments(),
+            profile.getUser(),
+            profile.getQueueName(),
+            computeDuration(profile.getStart(), profile.getPlanEnd()),
+            computeDuration(profile.getPlanEnd(), assumedQueueEndTime),
+            computeDuration(assumedQueueEndTime, profile.getEnd()),
+            profile.getState().name(),
+            profile.getQuery()
+         );
+      }
+    });
+  }
+
+  @Override
+  public boolean hasNext() {
+    return itr.hasNext();
+  }
+
+  @Override
+  public Object next() {
+    return itr.next();
+  }
+
+  @Override
+  public void remove() {
+    throw new UnsupportedOperationException();
+  }
+
+  public static class ProfileInfo {
+    private static final String UnknownValue = "N/A";
+
+    private static final ProfileInfo DEFAULT = new ProfileInfo();
+
+    public final String queryId;
+    public final Timestamp startTime;
+    public final String foreman;
+    public final long fragments;
+    public final String user;
+    public final String queue;
+    public final long planTime;
+    public final long queueTime;
+    public final long executeTime;
+    public final long totalTime;
+    public final String state;
+    public final String query;
+
+    public ProfileInfo(String query_id, Timestamp time, String foreman, long fragmentCount, String username,
+        String queueName, long planDuration, long queueWaitDuration, long executeDuration,
+        String state, String query) {
+      this.queryId = query_id;
+      this.startTime = time;
+      this.foreman = foreman;
+      this.fragments = fragmentCount;
+      this.user = username;
+      this.queue = queueName;
+      this.planTime = planDuration;
+      this.queueTime = queueWaitDuration;
+      this.executeTime = executeDuration;
+      this.totalTime = this.planTime + this.queueTime + this.executeTime;
+      this.query = query;
+      this.state = state;
+    }
+
+    private ProfileInfo() {
+      this(UnknownValue, new Timestamp(0), UnknownValue, 0L, UnknownValue, UnknownValue, 0L, 0L, 0L, UnknownValue, UnknownValue);
+    }
+
+    /**
+     * If unable to get ProfileInfo, use this default instance instead.
+     * @return the default instance
+     */
+    public static final ProfileInfo getDefault() {
+      return DEFAULT;
+    }
+  }
+}
+
+

http://git-wip-us.apache.org/repos/asf/drill/blob/13a568a5/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/ProfileIterator.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/ProfileIterator.java b/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/ProfileIterator.java
index 7152f45..f1d2075 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/ProfileIterator.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/ProfileIterator.java
@@ -17,125 +17,73 @@
  */
 package org.apache.drill.exec.store.sys;
 
-import com.google.common.base.Function;
-import com.google.common.collect.Iterators;
-import org.apache.drill.exec.ops.FragmentContext;
-import org.apache.drill.exec.proto.UserBitShared;
-import org.apache.drill.exec.work.foreman.QueryManager;
-
-import javax.annotation.Nullable;
-import java.sql.Timestamp;
 import java.util.Iterator;
-import java.util.Map;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map.Entry;
+
+import org.apache.drill.exec.ExecConstants;
+import org.apache.drill.exec.ops.FragmentContext;
+import org.apache.drill.exec.proto.UserBitShared.QueryProfile;
+import org.apache.drill.exec.server.QueryProfileStoreContext;
+import org.apache.drill.exec.server.options.OptionManager;
+import org.apache.drill.exec.util.ImpersonationUtil;
 
 /**
- * DRILL-5068: Add a new system table for completed profiles
+ * Base class for Profile Iterators
  */
-public class ProfileIterator implements Iterator<Object> {
-  static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ProfileIterator.class);
-
-  private final Iterator<ProfileInfo> itr;
+public abstract class ProfileIterator implements Iterator<Object> {
+  protected final QueryProfileStoreContext profileStoreContext;
+  protected final String queryingUsername;
+  protected final boolean isAdmin;
 
   public ProfileIterator(FragmentContext context) {
-    itr = iterateProfileInfo(context);
-  }
-
-  private Iterator<ProfileInfo> iterateProfileInfo(FragmentContext context) {
-    try {
-      PersistentStore<UserBitShared.QueryProfile> profiles = context
+    //Grab profile Store Context
+    profileStoreContext = context
         .getDrillbitContext()
-        .getStoreProvider()
-        .getOrCreateStore(QueryManager.QUERY_PROFILE);
-
-      return transform(profiles.getAll());
-
-    } catch (Exception e) {
-      logger.error(String.format("Unable to get persistence store: %s, ", QueryManager.QUERY_PROFILE.getName()), e);
-      return Iterators.singletonIterator(ProfileInfo.getDefault());
-    }
-  }
-
-  /**
-   * Iterating persistentStore as a iterator of {@link org.apache.drill.exec.store.sys.ProfileIterator.ProfileInfo}.
-   */
-  private Iterator<ProfileInfo> transform(Iterator<Map.Entry<String, UserBitShared.QueryProfile>> all) {
-    return Iterators.transform(all, new Function<Map.Entry<String, UserBitShared.QueryProfile>, ProfileInfo>() {
-      @Nullable
-      @Override
-      public ProfileInfo apply(@Nullable Map.Entry<String, UserBitShared.QueryProfile> input) {
-        if (input == null || input.getValue() == null) {
-          return ProfileInfo.getDefault();
-        }
+        .getProfileStoreContext();
 
-        final String queryID = input.getKey();
-
-        return new ProfileInfo(queryID,
-          mkHref(queryID),
-          new Timestamp(input.getValue().getStart()),
-          input.getValue().getEnd() - input.getValue().getStart(),
-          input.getValue().getUser(),
-          input.getValue().getQuery(),
-          input.getValue().getState().name()
-        );
-      }
-
-      /**
-       * Generate a link to detailed profile page using queryID. this makes user be able to jump to that page directly from query result.
-       * @param queryID query ID
-       * @return html href link of the input query ID
-       */
-      private String mkHref(String queryID) {
-        return String.format("<a href=\"/profiles/%s\">%s</a>", queryID, queryID);
-      }
-    });
-  }
-
-  @Override
-  public boolean hasNext() {
-    return itr.hasNext();
+    queryingUsername = context.getQueryUserName();
+    isAdmin = hasAdminPrivileges(context);
   }
 
-  @Override
-  public Object next() {
-    return itr.next();
-  }
+  protected boolean hasAdminPrivileges(FragmentContext context) {
+    OptionManager options = context.getOptions();
+    if (context.isUserAuthenticationEnabled() &&
+        !ImpersonationUtil.hasAdminPrivileges(
+          context.getQueryUserName(),
+          ExecConstants.ADMIN_USERS_VALIDATOR.getAdminUsers(options),
+          ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.getAdminUserGroups(options))) {
+      return false;
+    }
 
-  @Override
-  public void remove() {
-    throw new UnsupportedOperationException();
+    //Passed checks
+    return true;
   }
 
-  public static class ProfileInfo {
-    private static final ProfileInfo DEFAULT = new ProfileInfo();
-
-    public final String query_id;
-    public final String link;
-    public final Timestamp time;
-    public final long latency;
-    public final String user;
-    public final String query;
-    public final String state;
-
-    public ProfileInfo(String query_id, String link, Timestamp time, long latency, String user, String query, String state) {
-      this.query_id = query_id;
-      this.link = link;
-      this.time = time;
-      this.latency = latency;
-      this.user = user;
-      this.query = query;
-      this.state = state;
+  //Returns an iterator for authorized profiles
+  protected Iterator<Entry<String, QueryProfile>> getAuthorizedProfiles (
+      Iterator<Entry<String, QueryProfile>> allProfiles, String username, boolean isAdministrator) {
+    if (isAdministrator) {
+      return allProfiles;
     }
 
-    private ProfileInfo() {
-      this("UNKNOWN", "UNKNOWN", new Timestamp(0L), 0L, "UNKNOWN", "UNKNOWN", "UNKNOWN");
+    List<Entry<String, QueryProfile>> authorizedProfiles = new LinkedList<Entry<String, QueryProfile>>();
+    while (allProfiles.hasNext()) {
+      Entry<String, QueryProfile> profileKVPair = allProfiles.next();
+      //Check if user matches
+      if (profileKVPair.getValue().getUser().equals(username)) {
+        authorizedProfiles.add(profileKVPair);
+      }
     }
+    return authorizedProfiles.iterator();
+  }
 
-    /**
-     * If unable to get ProfileInfo, use this default instance instead.
-     * @return the default instance
-     */
-    public static final ProfileInfo getDefault() {
-      return DEFAULT;
+  protected long computeDuration(long startTime, long endTime) {
+    if (endTime > startTime && startTime > 0) {
+      return (endTime - startTime);
+    } else {
+      return 0;
     }
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/13a568a5/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/ProfileJsonIterator.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/ProfileJsonIterator.java b/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/ProfileJsonIterator.java
new file mode 100644
index 0000000..fcc2921
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/ProfileJsonIterator.java
@@ -0,0 +1,139 @@
+/**
+ * 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.drill.exec.store.sys;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import javax.annotation.Nullable;
+
+import org.apache.drill.exec.ops.FragmentContext;
+import org.apache.drill.exec.proto.UserBitShared;
+import org.apache.drill.exec.proto.UserBitShared.QueryProfile;
+import org.apache.drill.exec.serialization.InstanceSerializer;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterators;
+
+/**
+ * System table listing completed profiles as JSON documents
+ */
+public class ProfileJsonIterator extends ProfileIterator {
+  static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ProfileJsonIterator.class);
+
+  private final InstanceSerializer<QueryProfile> profileSerializer;
+  private final Iterator<ProfileJson> itr;
+
+  public ProfileJsonIterator(FragmentContext context) {
+    super(context);
+    //Holding a serializer (for JSON extract)
+    profileSerializer = profileStoreContext.
+        getProfileStoreConfig().getSerializer();
+
+    itr = iterateProfileInfoJson();
+  }
+
+  //Returns an iterator for authorized profiles
+  private Iterator<ProfileJson> iterateProfileInfoJson() {
+    try {
+      //Transform authorized profiles to iterator for ProfileInfoJson
+      return transformJson(
+          getAuthorizedProfiles(
+            profileStoreContext
+              .getCompletedProfileStore()
+              .getAll(),
+            queryingUsername, isAdmin));
+
+    } catch (Exception e) {
+      logger.error(e.getMessage());
+      return Iterators.singletonIterator(ProfileJson.getDefault());
+    }
+  }
+
+  /**
+   * Iterating persistentStore as a iterator of {@link org.apache.drill.exec.store.sys.ProfileJsonIterator.ProfileJson}.
+   */
+  private Iterator<ProfileJson> transformJson(Iterator<Entry<String, UserBitShared.QueryProfile>> all) {
+    return Iterators.transform(all, new Function<Entry<String, UserBitShared.QueryProfile>, ProfileJson>() {
+      @Nullable
+      @Override
+      public ProfileJson apply(@Nullable Entry<String, UserBitShared.QueryProfile> input) {
+        if (input == null || input.getValue() == null) {
+          return ProfileJson.getDefault();
+        }
+
+        //Constructing ProfileInfo
+        final String queryID = input.getKey();
+        String profileJson = null;
+        try {
+          profileJson = new String(profileSerializer.serialize(input.getValue()));
+        } catch (IOException e) {
+          logger.debug("Failed to serialize profile for: " + queryID);
+          profileJson = "{ 'message' : 'error (unable to serialize profile: "+ queryID +")' }";
+        }
+
+        return new ProfileJson(
+            queryID,
+            profileJson
+         );
+      }
+    });
+  }
+
+  @Override
+  public boolean hasNext() {
+    return itr.hasNext();
+  }
+
+  @Override
+  public Object next() {
+    return itr.next();
+  }
+
+  @Override
+  public void remove() {
+    throw new UnsupportedOperationException();
+  }
+
+  public static class ProfileJson {
+    private static final String UnknownValue = "N/A";
+
+    private static final ProfileJson DEFAULT = new ProfileJson();
+
+    public final String queryId;
+    public final String json;
+
+    public ProfileJson(String query_id, String profileJson) {
+      this.queryId = query_id;
+      this.json = profileJson;
+    }
+
+    private ProfileJson() {
+      this(UnknownValue, UnknownValue);
+    }
+
+    /**
+     * If unable to get ProfileInfo, use this default instance instead.
+     * @return the default instance
+     */
+    public static final ProfileJson getDefault() {
+      return DEFAULT;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/13a568a5/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/SystemTable.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/SystemTable.java b/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/SystemTable.java
index 12739f7..8803304 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/SystemTable.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/SystemTable.java
@@ -87,17 +87,24 @@ public enum SystemTable {
     }
   },
 
-  THREADS("threads", true, ThreadsIterator.ThreadsInfo.class) {
+  PROFILES("profiles", false, ProfileInfoIterator.ProfileInfo.class) {
     @Override
-  public Iterator<Object> getIterator(final FragmentContext context) {
-      return new ThreadsIterator(context);
+    public Iterator<Object> getIterator(final FragmentContext context) {
+      return new ProfileInfoIterator(context);
+    }
+  },
+
+  PROFILES_JSON("profiles_json", false, ProfileJsonIterator.ProfileJson.class) {
+    @Override
+    public Iterator<Object> getIterator(final FragmentContext context) {
+      return new ProfileJsonIterator(context);
     }
   },
 
-  PROFILES("profiles", false, ProfileIterator.ProfileInfo.class) {
+  THREADS("threads", true, ThreadsIterator.ThreadsInfo.class) {
     @Override
-    public Iterator<Object> getIterator(FragmentContext context) {
-      return new ProfileIterator(context);
+  public Iterator<Object> getIterator(final FragmentContext context) {
+      return new ThreadsIterator(context);
     }
   };
 

http://git-wip-us.apache.org/repos/asf/drill/blob/13a568a5/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/store/LocalPersistentStore.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/store/LocalPersistentStore.java b/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/store/LocalPersistentStore.java
index 320a864..313a9be 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/store/LocalPersistentStore.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/store/sys/store/LocalPersistentStore.java
@@ -123,6 +123,7 @@ public class LocalPersistentStore<V> extends BasePersistentStore<V> {
             return path.getName().endsWith(DRILL_SYS_FILE_SUFFIX);
           }
         };
+
         List<FileStatus> fileStatuses = DrillFileSystemUtil.listFiles(fs, basePath, false, sysFileSuffixFilter);
         if (fileStatuses.isEmpty()) {
           return Collections.emptyIterator();
@@ -135,6 +136,7 @@ public class LocalPersistentStore<V> extends BasePersistentStore<V> {
         }
 
         Collections.sort(files);
+
         return Iterables.transform(Iterables.limit(Iterables.skip(files, skip), take), new Function<String, Entry<String, V>>() {
           @Nullable
           @Override

http://git-wip-us.apache.org/repos/asf/drill/blob/13a568a5/exec/java-exec/src/test/java/org/apache/drill/exec/store/sys/TestSystemTable.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/store/sys/TestSystemTable.java b/exec/java-exec/src/test/java/org/apache/drill/exec/store/sys/TestSystemTable.java
index d2161f7..d39d2ec 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/exec/store/sys/TestSystemTable.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/store/sys/TestSystemTable.java
@@ -74,4 +74,9 @@ public class TestSystemTable extends BaseTestQuery {
   public void profilesTable() throws Exception {
     test("select * from sys.profiles");
   }
+
+  @Test
+  public void profilesJsonTable() throws Exception {
+    test("select * from sys.profiles_json");
+  }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/drill/blob/13a568a5/exec/java-exec/src/test/java/org/apache/drill/exec/work/metadata/TestMetadataProvider.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/work/metadata/TestMetadataProvider.java b/exec/java-exec/src/test/java/org/apache/drill/exec/work/metadata/TestMetadataProvider.java
index 37aa1db..b2240fc 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/exec/work/metadata/TestMetadataProvider.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/work/metadata/TestMetadataProvider.java
@@ -149,7 +149,7 @@ public class TestMetadataProvider extends BaseTestQuery {
 
     assertEquals(RequestStatus.OK, resp.getStatus());
     List<TableMetadata> tables = resp.getTablesList();
-    assertEquals(14, tables.size());
+    assertEquals(17, tables.size());
 
     verifyTable("INFORMATION_SCHEMA", "CATALOGS", tables);
     verifyTable("INFORMATION_SCHEMA", "COLUMNS", tables);
@@ -186,7 +186,7 @@ public class TestMetadataProvider extends BaseTestQuery {
 
     assertEquals(RequestStatus.OK, resp.getStatus());
     List<TableMetadata> tables = resp.getTablesList();
-    assertEquals(14, tables.size());
+    assertEquals(17, tables.size());
 
     verifyTable("INFORMATION_SCHEMA", "CATALOGS", tables);
     verifyTable("INFORMATION_SCHEMA", "COLUMNS", tables);
@@ -214,7 +214,7 @@ public class TestMetadataProvider extends BaseTestQuery {
 
     assertEquals(RequestStatus.OK, resp.getStatus());
     List<TableMetadata> tables = resp.getTablesList();
-    assertEquals(7, tables.size());
+    assertEquals(10, tables.size());
 
     verifyTable("sys", "boot", tables);
     verifyTable("sys", "memory", tables);
@@ -248,7 +248,7 @@ public class TestMetadataProvider extends BaseTestQuery {
 
     assertEquals(RequestStatus.OK, resp.getStatus());
     List<ColumnMetadata> columns = resp.getColumnsList();
-    assertEquals(93, columns.size());
+    assertEquals(118, columns.size());
     // too many records to verify the output.
   }