<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Distributed Musings]]></title><description><![CDATA[Distributed Musings]]></description><link>https://www.sestevez.com/</link><image><url>http://www.sestevez.com/favicon.png</url><title>Distributed Musings</title><link>https://www.sestevez.com/</link></image><generator>Ghost 2.38</generator><lastBuildDate>Wed, 15 Apr 2026 10:51:41 GMT</lastBuildDate><atom:link href="https://www.sestevez.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Agency Swarm with Third Party and Open Source Models]]></title><description><![CDATA[I have recently been answering a bunch of questions from agency swarm users looking to leverage Astra Assistants. Agency swarm is built ontop of OpenAI's Assistants API and folks have been voicing desire to use it with other model providers and with open source models.]]></description><link>https://www.sestevez.com/agency-swarm-other-models/</link><guid isPermaLink="false">668f42c785031e0ab48728de</guid><category><![CDATA[assistants api]]></category><category><![CDATA[agents]]></category><category><![CDATA[agency swarm]]></category><category><![CDATA[genai]]></category><category><![CDATA[ollama]]></category><category><![CDATA[anthropic]]></category><category><![CDATA[groq]]></category><category><![CDATA[gemini]]></category><category><![CDATA[mistral]]></category><category><![CDATA[deepseek]]></category><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Thu, 11 Jul 2024 04:38:40 GMT</pubDate><media:content url="http://www.sestevez.com/content/images/2024/07/Screenshot-from-2024-07-11-00-23-02.png" medium="image"/><content:encoded><![CDATA[<img src="http://www.sestevez.com/content/images/2024/07/Screenshot-from-2024-07-11-00-23-02.png" alt="Agency Swarm with Third Party and Open Source Models"><p>I have recently been answering a bunch of questions from <a href="https://github.com/VRSEN/agency-swarm">agency swarm</a> users looking to leverage <a href="https://github.com/datastax/astra-assistants-api">Astra Assistants</a>. </p><p>They fall into the following two categories:</p><!--kg-card-begin: markdown--><ul>
<li>I want to run agency swarm with other model providers via API (Anthropic, Google, Groq, etc.)</li>
<li>I want to run agency swarm with open source models on my local machine / infrastructure</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="agencywhat">Agency what?</h2>
<p>Agency swarm is a popular multi agent framework built on top of OpenAI's Assistants API.</p>
<p>Folks have been voicing desire to use it with other model providers and with open source models. They even recommend <a href="https://vrsen.github.io/agency-swarm/advanced-usage/open-source-models/">Astra Assistants in their docs</a>.</p>
<p>I watched some of <a href="https://www.youtube.com/@vrsen">VRSEN's</a> videos on youtube and some of them remind me of the phrase &quot;the future is here, it's just not evenly distributed&quot;.</p>
<p>He is out there talking about automating business processes not only using AI agents but groups of them (which he calls agencies) and yet all his stuff is very grounded in reality.</p>
<p>There are a couple of things about his framework that I deeply agree with:</p>
<ol>
<li>No hard coded framework prompts</li>
<li>Pydantic / Instructor powered type checking for tool creation</li>
<li>Commitment to OpenAI's Assistant API as the right level of abstraction both for setting up and scaling agents</li>
</ol>
<p>If you haven't checked out agency-swarm, I recommend having a look at the github repo and some of VRSEN's videos.</p>
<p>If you're here to find out how to set up Agency Swarm to work with Astra Assistants, read on:</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="otherprovidersviaapi">Other providers via API</h2>
<!--kg-card-end: markdown--><p>If all you are looking at doing is leveraging agency-swarm with other providers (for example Anthropic), simply set up your .env file with the api keys for the provider and wrap your openai client with the following <a href="https://github.com/datastax/astra-assistants-api/blob/main/examples/python/agency-swarm/third_party_provider_apis.py">sample code</a>:</p><!--kg-card-begin: markdown--><pre><code>from openai import OpenAI
from astra_assistants import patch
from agency_swarm import Agent, Agency, set_openai_client
from dotenv import load_dotenv

load_dotenv(&quot;./.env&quot;)
load_dotenv(&quot;../../../.env&quot;)

client = patch(OpenAI())

set_openai_client(client)

ceo = Agent(name=&quot;CEO&quot;,
            description=&quot;Responsible for client communication, task planning, and management.&quot;,
            instructions=&quot;Please communicate with users and other agents.&quot;,
            model=&quot;anthropic/claude-3-haiku-20240307&quot;,
            # model=&quot;gpt-3.5-turbo&quot;,
            files_folder=&quot;./examples/python/agency-swarm/files&quot;,
            tools=[])

agency = Agency([ceo])

assistant = client.beta.assistants.retrieve(ceo.id)
print(assistant)

completion = agency.get_completion(&quot;What's something interesting about language models?&quot;)
print(completion)
</code></pre>
<!--kg-card-end: markdown--><p>your <a href="https://github.com/datastax/astra-assistants-api/blob/main/client/.env.bkp">.env file</a> may look like this:</p><!--kg-card-begin: markdown--><pre><code>#!/bin/bash

# AstraDB -&gt; https://astra.datastax.com/ --&gt; tokens --&gt; administrator user --&gt; generate
export ASTRA_DB_APPLICATION_TOKEN=&quot;&quot;

# OpenAI Models - https://platform.openai.com/api-keys --&gt; create new secret key
export OPENAI_API_KEY=&quot;fake&quot;

# Anthropic claude models - https://console.anthropic.com/settings/keys
export ANTHROPIC_API_KEY=&quot;&lt;insert key here&gt;&quot;
</code></pre>
<!--kg-card-end: markdown--><p>Note on architecture. You do not have to run the Astra Assistants backend yourself, the client library will point you at the hosted astra-assistants API hosted by DataStax. However the code is open source, Apache 2 licensed and you can choose to self host if you so choose.</p><!--kg-card-begin: markdown--><h2 id="localmodels">Local Models</h2>
<!--kg-card-end: markdown--><p>If you're running inference locally or in your own private infrastructure, you will have to run the Astra Assistants backend yourself so as to be able to point to your inference server for completions.</p><p>The simplest approach is to use ollama and leverage the <a href="https://github.com/datastax/astra-assistants-api/tree/main/examples/ollama">docker-compose yamls in the Astra Assistants repo</a>.</p><p>There are two versions, with and without GPU support. We'll look at GPU support since it's more performant and slightly more complex. See the docker-compose.yaml below: </p><!--kg-card-begin: markdown--><pre><code>version: '3.8'

services:
  ollama:
    image: ollama/ollama
    ports:
      - &quot;11434:11434&quot;
    networks:
      - my_network
    volumes:
      - ~/.ollama:/root/.ollama  #map to local volume to keep models
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [ gpu ]
    environment:
      NVIDIA_VISIBLE_DEVICES: &quot;all&quot;  # or specify the GPU IDs
    runtime: nvidia  # Specify the runtime for NVIDIA GPUs  -

  assistants:
    image: datastax/astra-assistants
    ports:
      - &quot;8080:8000&quot;
    networks:
      - my_network
    depends_on:
      - ollama


networks:
  my_network:
    driver: bridge
</code></pre>
<!--kg-card-end: markdown--><p>Notice the networks section which ensures that your containers can talk to each other. You can ensure this is working properly by exec'ing into the assistants container and running:</p><!--kg-card-begin: markdown--><pre><code>curl http://ollama:11434
</code></pre>
<!--kg-card-end: markdown--><p>Note: in this setup you need to point to ollama in your application code using the LLM-PARAM-base-url header as per this <a href="https://github.com/datastax/astra-assistants-api/blob/main/examples/python/agency-swarm/local_open_source_models.py">example</a> when you wrap the client:</p><!--kg-card-begin: markdown--><pre><code>from openai import OpenAI
from astra_assistants import patch
from agency_swarm import Agent, Agency, set_openai_client
from dotenv import load_dotenv

load_dotenv(&quot;./.env&quot;)
load_dotenv(&quot;../../../.env&quot;)

# client = patch(OpenAI(default_headers={&quot;LLM-PARAM-base-url&quot;: &quot;http://localhost:11434&quot;}))
# if using docker-compose, pass custom header to point to the ollama container instead of localhost
client = patch(OpenAI(default_headers={&quot;LLM-PARAM-base-url&quot;: &quot;http://ollama:11434&quot;}))

set_openai_client(client)

ceo = Agent(name=&quot;CEO&quot;,
            description=&quot;Responsible for client communication, task planning, and management.&quot;,
            instructions=&quot;Please communicate with users and other agents.&quot;,
            model=&quot;ollama_chat/deepseek-coder-v2&quot;, # ensure that the model has been pulled in ollama
            files_folder=&quot;./examples/python/agency-swarm/files&quot;,
            tools=[])

agency = Agency([ceo])

assistant = client.beta.assistants.retrieve(ceo.id)
print(assistant)

completion = agency.get_completion(&quot;What's something interesting about language models?&quot;)
print(completion)
</code></pre>
<!--kg-card-end: markdown--><p>If you were running ollama and astra-assistants directly on your host (or with docker using host networking) you would point to localhost:</p><!--kg-card-begin: markdown--><pre><code>default_headers={&quot;LLM-PARAM-base-url&quot;: &quot;http://localhost:11434&quot;}
</code></pre>
<!--kg-card-end: markdown--><p>UPDATE: In astra-assistants 2.0.13 I added support for `OLLAMA_API_BASE_URL` which replaces the LLM-PARAM-base-url setting. Not only is the env var more convenient but it also allows it to work with complex agencies that leverage both ollama and API provider based models.</p><!--kg-card-begin: markdown--><h2 id="noteonlitellm">Note on LiteLLM</h2>
<p>Adding this note because I have been asked this question multiple times. LiteLLM proxy with Astra Assistants will be supported when <a href="https://github.com/BerriAI/litellm/pull/4118/">this PR</a> gets merged.</p>
<p>That said, Astra Assistants uses litellm as a library to route LLM completions so the proxy is not strictly necessary to get agency swarm working with other models.</p>
<p>Note, other features of LiteLLM Proxy like cost tracking, etc. will not be available with this method.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[What is Astra Assistants]]></title><description><![CDATA[Astra Assistants is a drop in replacement for OpenAI's Assistants API that supports third party LLMs and embedding models and uses AstraDB / Apache Cassandra for persistence and ANN. You can use our managed service on Astra or you can host it yourself since it's open source.]]></description><link>https://www.sestevez.com/what-is-astra-assistants/</link><guid isPermaLink="false">668eae9585031e0ab487277e</guid><category><![CDATA[openai]]></category><category><![CDATA[genai]]></category><category><![CDATA[assistants api]]></category><category><![CDATA[astra assistants]]></category><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Thu, 11 Jul 2024 02:22:40 GMT</pubDate><media:content url="http://www.sestevez.com/content/images/2024/07/create_assistant-2.gif" medium="image"/><content:encoded><![CDATA[<img src="http://www.sestevez.com/content/images/2024/07/create_assistant-2.gif" alt="What is Astra Assistants"><p>Last November, the week before my daughter was born, OpenAI released the Assistants API. I tried building an app with it and was impressed with the simplicity and power of the abstraction so I decided to start sleep deprivation early and built v0 of `<a href="https://github.com/datastax/astra-assistants-api">astra-assistants</a>`.</p><p>Astra Assistants is a drop in replacement for OpenAI's Assistants API that supports third party LLMs and embedding models and uses AstraDB / Apache Cassandra for persistence and ANN. You can use our managed service on <a href="https://docs.datastax.com/en/astra-db-serverless/tutorials/astra-assistants-api.html">Astra</a> or you can host it yourself since it's open source.</p><!--kg-card-begin: markdown--><p><strong>Note:</strong> If you like the project please give us a github star! <a href="https://github.com/datastax/astra-assistants-api/stargazers"><img src="https://img.shields.io/github/stars/datastax/astra-assistants-api?style=social" alt="What is Astra Assistants"></a> and join us on Discord! <a href="https://discord.gg/MEFVXUvsuy"><img src="https://img.shields.io/static/v1?label=Chat%20on&amp;message=Discord&amp;color=blue&amp;logo=Discord&amp;style=flat-square" alt="What is Astra Assistants"></a></p>
<!--kg-card-end: markdown--><p>As you can see below, you can simply patch your OpenAI client with the assistants client library and pick your model. This will point your app at our managed astra-assistants service instead of at OpenAI.</p><!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="http://www.sestevez.com/content/images/2024/07/create_assistant.gif" class="kg-image" alt="What is Astra Assistants"><figcaption>astra asssistants works with third party models like claude-3-5-sonnet, gemini 1.5, command-r-plus, and even local ollama models with a single line of code</figcaption></figure><!--kg-card-end: image--><p>Astra Assistants will automatically route LLM and embedding calls to your model provider of choice using <a href="https://github.com/BerriAI/litellm">LiteLLM</a> and it will persist your threads, messages, assistants, vector_stores, files, etc. to <a href="https://astra.datastax.com/">AstraDB</a>. File search leverages AstraDB's vector functionality powered by <a href="https://github.com/jbellis/jvector">jvector</a>. </p><p>For authentication you must provide corresponding api keys for the model provider(s). We recommend using environment variables in a <a href="https://github.com/datastax/astra-assistants-api/blob/main/client/.env.bkp">.env</a> file which automatically get picked up and sent to the astra-assistants service as http request headers by the astra-assistants client library when you patch the OpenAI sdk. </p><!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="http://www.sestevez.com/content/images/2024/07/environment_vars.png" class="kg-image" alt="What is Astra Assistants"><figcaption>sample .env file with some api keys</figcaption></figure><!--kg-card-end: image--><!--kg-card-begin: markdown--><h2 id="architecture">Architecture</h2>
<!--kg-card-end: markdown--><p>Astra Assistants is a python project built on fastapi that implements the backend for Assistants API using the Cassandra python driver and <a href="https://github.com/BerriAI/litellm">LiteLLM</a>.</p><!--kg-card-begin: image--><figure class="kg-card kg-image-card"><img src="http://www.sestevez.com/content/images/2024/07/Astra-Assistants-Architecture---Page-1--1-.png" class="kg-image" alt="What is Astra Assistants"></figure><!--kg-card-end: image--><p>If you run astra-assistants yourself you can even point to your<a href="https://github.com/datastax/astra-assistants-api/tree/main/examples/ollama"> local ollama setup </a>for use with open source models.</p><!--kg-card-begin: markdown--><h2 id="releaseandimprovements">Release and improvements</h2>
<!--kg-card-end: markdown--><p>We launched the service on November 15th 2023:</p><!--kg-card-begin: bookmark--><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://www.datastax.com/blog/introducing-the-astra-assistants-api"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Introducing the Astra Assistants API | DataStax</div><div class="kg-bookmark-description">Learn about the new Astra Assistants API</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.datastax.com/favicon-32x32.png" alt="What is Astra Assistants"><span class="kg-bookmark-author">Sebastian Estevez</span><span class="kg-bookmark-publisher">DataStax</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.sanity.io/images/bbnkhnhl/production/cb796cf1be66d5579c52d97344c5bfea727cef5b-1460x968.jpg" alt="What is Astra Assistants"></div></a><figcaption>astra assistants hosted service was announced on November 16th</figcaption></figure><!--kg-card-end: bookmark--><p>We added streaming support in February 2024 (before OpenAI):</p><!--kg-card-begin: bookmark--><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.datastax.com/blog/astra-assistants-api-now-supports-streaming-because-who-wants-to-wait"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Astra Assistants API Now Supports Streaming: Because Who Wants to Wait? | DataStax</div><div class="kg-bookmark-description">DataStax announces support for OpenAI style streaming runs in Astra Assistants--it is available both in the managed service and the open source codebase.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.datastax.com/favicon-32x32.png" alt="What is Astra Assistants"><span class="kg-bookmark-author">Sebastian Estevez</span><span class="kg-bookmark-publisher">DataStax</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.sanity.io/images/bbnkhnhl/production/90fba45a7ed351ca2571036922af73ad5401a72a-1460x968.jpg" alt="What is Astra Assistants"></div></a></figure><!--kg-card-end: bookmark--><p>We open sourced the server side code in March of 2024</p><!--kg-card-begin: bookmark--><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.datastax.com/blog/astra-assistants-api-is-open-source"><div class="kg-bookmark-content"><div class="kg-bookmark-title">The Astra Assistants API Is Now Open Source | DataStax</div><div class="kg-bookmark-description">We’re excited to announce that the Astra Assistants API server, our drop-in replacement for the OpenAI Assistants API, is now open source.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.datastax.com/favicon-32x32.png" alt="What is Astra Assistants"><span class="kg-bookmark-author">Sebastian Estevez</span><span class="kg-bookmark-publisher">DataStax</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.sanity.io/images/bbnkhnhl/production/70d15ed1c6bb9e9ad85690782b00d948c116b94c-1460x968.jpg" alt="What is Astra Assistants"></div></a></figure><!--kg-card-end: bookmark--><p></p><p>And we added support for assistants v2 (including vector_stores) in <a href="https://www.linkedin.com/posts/sestevez_astra-assistants-v2-update-its-been-a-activity-7202537987807592448-CFHR?utm_source=share&amp;utm_medium=member_desktop">June of 2024</a>.</p><!--kg-card-begin: markdown--><h2 id="conclusion">Conclusion</h2>
<!--kg-card-end: markdown--><p>It's been a ton of fun working on Astra Assistants and I'll continue to post updates here so stay tuned!</p><!--kg-card-begin: markdown--><p>If you like the project please give us a github star! <a href="https://github.com/datastax/astra-assistants-api/stargazers"><img src="https://img.shields.io/github/stars/datastax/astra-assistants-api?style=social" alt="What is Astra Assistants"></a> and join us on Discord! <a href="https://discord.gg/MEFVXUvsuy"><img src="https://img.shields.io/static/v1?label=Chat%20on&amp;message=Discord&amp;color=blue&amp;logo=Discord&amp;style=flat-square" alt="What is Astra Assistants"></a></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Connecting to Astra from DataGrip via JDBC]]></title><description><![CDATA[<p>A few users have been asking me lately about connecting to DataStax Astra from different developer tools. As a result I am planning to do a series of quick posts around these starting with Intellij DataGrip. Big thank you to Donnie Roberson and Nick Panahi for their help getting the</p>]]></description><link>https://www.sestevez.com/astra-datagrip/</link><guid isPermaLink="false">5f244131c478d72871fb8958</guid><category><![CDATA[astra]]></category><category><![CDATA[cassandra]]></category><category><![CDATA[datagrip]]></category><category><![CDATA[intellij]]></category><category><![CDATA[developer tools]]></category><category><![CDATA[jdbc]]></category><category><![CDATA[datastax-java-driver]]></category><category><![CDATA[dbaas]]></category><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Fri, 31 Jul 2020 18:16:35 GMT</pubDate><media:content url="http://www.sestevez.com/content/images/2020/07/dg-astra.png" medium="image"/><content:encoded><![CDATA[<img src="http://www.sestevez.com/content/images/2020/07/dg-astra.png" alt="Connecting to Astra from DataGrip via JDBC"><p>A few users have been asking me lately about connecting to DataStax Astra from different developer tools. As a result I am planning to do a series of quick posts around these starting with Intellij DataGrip. Big thank you to Donnie Roberson and Nick Panahi for their help getting the material ready for these posts.</p><p>Although I'm a fan of IDEA, Go Land, and GoLion, I confess I had not played with DataGrip until recently. I have found it to be a solid product. The kind of thing you'd expect from our friends at IntelliJ.</p><h3 id="i-thought-datagrip-supported-cassandra-out-of-the-box">I thought DataGrip supported Cassandra out of the box</h3><p>Yes this is true, there is an Apache Cassandra that ships with DataGrip. However, Astra is secure by default and the easiest way to connect to Astra is to use the <a href="https://docs.astra.datastax.com/docs/obtaining-database-credentials">secure connect bundle</a> which effortlessly gives us mTLS. For this we need a JDBC driver that is built ontop of a modern version of the DataStax Java driver. Fortunately DataGrip allows us to add custom JDBC drivers with relative ease.</p><p>It might be possible to unpack the secure connect bundle and pick out all the pieces needed to configure SSL with the DataGrip cassandra driver but that will be a topic for another day.</p><h3 id="grab-and-unzip-the-driver">Grab and unzip the driver</h3><p>Downoad the DataStax JDBC Driver which you can download <a href="https://downloads.datastax.com/jdbc/cql/2.0.4.1004/SimbaCassandraJDBC42-2.0.4.1004.zip" rel="nofollow noopener noreferrer">here</a> or at <a href="https://downloads.datastax.com/#odbc-jdbc-drivers">https://downloads.datastax.com/#odbc-jdbc-drivers</a>. I'm using version 2.0.4.</p><p>Next unzip that file in your Downloads directory</p><p>    unzip SimbaCassandraJDBC42-2.0.4.1004.zip</p><h3 id="import-ide-settings">Import IDE Settings</h3><p>Download and import these <a href="https://datastax-21b7c7df5342.intercom-attachments-7.com/i/o/232268459/929cbfa881f4423cceb8b3b2/settings.zip" rel="noopener noreferrer">settings.zip</a> into DataGrip.</p><p>**Note**: If you are already a heavy DataGrip user, make sure to back up your existing settings and proceed with caution!</p><p>File –&gt; Manage IDE Settings –&gt; Import Settings or simply triple shift – &gt; import settings</p><p>At this point you should be able to see a new database connection type called Astra:</p><figure class="kg-card kg-image-card"><img src="https://downloads.intercomcdn.com/i/o/232269523/634681a503a86fc35eebd711/image.png" class="kg-image" alt="Connecting to Astra from DataGrip via JDBC"><figcaption>Astra DB connection</figcaption></figure><p>If you would rather keep our JDBC jar elsewhere, just remember to change the path under driver files to match where you unziped your jar. By default it will use your user home `/Downloads` directory .</p><h3 id="establish-the-connection">Establish the connection </h3><p>When you create your connection, URL should look like this:    <code>jdbc:cassandra://;AuthMech=2;UID=&lt;YOUR USER ID&gt;;PWD=&lt;YOUR PASSWORD&gt;;SecureConnectionBundlePath=&lt;PATH TO YOUR SECURE CONNECT BUNDLE&gt;;TunableConsistency=6</code> </p><h3 id="it-works-">It works!</h3><p>At this point you can do things like create tables, introspect your keyspaces, view your data in the DataGrip table explorer and more.</p><figure class="kg-card kg-image-card"><img src="http://www.sestevez.com/content/images/2020/07/image.png" class="kg-image" alt="Connecting to Astra from DataGrip via JDBC"><figcaption>Table explorer</figcaption></figure><figure class="kg-card kg-image-card"><img src="http://www.sestevez.com/content/images/2020/07/image-1.png" class="kg-image" alt="Connecting to Astra from DataGrip via JDBC"><figcaption>Execute queries</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[Astra and Akka-Peristence]]></title><description><![CDATA[<p>I've been chatting with a few folks that run akka-persistence backed by cassandra using either the <a href="https://github.com/akka/akka-persistence-cassandra">akka-persistence-cassandra</a> project directly or via the <a href="https://github.com/akka/akka-persistence-cassandra">Lagom micro services framework</a>. These folks are often big fans of cassandra's peer to peer distributed architecture, it's availability and performance capabilities, it's active active geo-reduncancy, and it's</p>]]></description><link>https://www.sestevez.com/astra-akka-lagom/</link><guid isPermaLink="false">5f062657c478d72871fb88e0</guid><category><![CDATA[akka-persistence-cassandra]]></category><category><![CDATA[cassandra]]></category><category><![CDATA[astra]]></category><category><![CDATA[lagom]]></category><category><![CDATA[scala]]></category><category><![CDATA[secure-connect-bundle]]></category><category><![CDATA[java-driver]]></category><category><![CDATA[datastax-java-driver]]></category><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Wed, 08 Jul 2020 21:18:52 GMT</pubDate><media:content url="http://www.sestevez.com/content/images/2020/07/logom.png" medium="image"/><content:encoded><![CDATA[<img src="http://www.sestevez.com/content/images/2020/07/logom.png" alt="Astra and Akka-Peristence"><p>I've been chatting with a few folks that run akka-persistence backed by cassandra using either the <a href="https://github.com/akka/akka-persistence-cassandra">akka-persistence-cassandra</a> project directly or via the <a href="https://github.com/akka/akka-persistence-cassandra">Lagom micro services framework</a>. These folks are often big fans of cassandra's peer to peer distributed architecture, it's availability and performance capabilities, it's active active geo-reduncancy, and it's scalability characteristics. </p><p>For a lot of these folks, running cassandra clusters is not their main passion. They'd rather spend time thinking about cqrs, event sourcing, actors, fancy scala things like <a href="http://fdahms.com/2015/10/14/scala-and-the-transient-lazy-val-pattern/">@transient</a>, and of course their business logic.</p><h2 id="zero-ops">Zero Ops?</h2><p>Today, you don't have to manage your own cassandra clusters, just use <a href="https://astra.datastax.com/">Astra</a>!</p><h2 id="dependency-fun">Dependency fun</h2><p>If you are a lagom or akka-persistence user trying to switch over to dbaas you may have landed at the astra java driver docs and seen that to use the secure-connect-bundle you need a recent version of the Cassandra Java Driver.</p><p>This is both true and not true, but first what even is this secure-connect-bundle thing?</p><h2 id="what-even-is-a-secure-connect-bundle">What even is a secure-connect-bundle?</h2><p>Connections to Astra are secure by default and support two way TLS over the wire. In order to achieve this, there are a multiple settings in the driver that need to be configured when instantiating your cql session.</p><p>To make things easier for users, Astra replaces this complex configuration with a single line that points to a compressed file (the secure-connect-bundle).</p><p>These are the Java driver versions that support scb (naturally newer dot versions will also support it):</p><p>DataStax Java driver for Apache Cassandra 4.x</p><p><code>&lt;dependency&gt;   &lt;groupId&gt;com.datastax.oss&lt;/groupId&gt;   &lt;artifactId&gt;java-driver-core&lt;/artifactId&gt;   &lt;version&gt;4.6.0&lt;/version&gt; &lt;/dependency&gt; </code></p><p>DataStax Java driver for Apache Cassandra 3.x</p><p><code>&lt;dependency&gt;   &lt;groupId&gt;com.datastax.cassandra&lt;/groupId&gt;   &lt;artifactId&gt;cassandra-driver-core&lt;/artifactId&gt;   &lt;version&gt;3.8.0&lt;/version&gt; &lt;/dependency&gt; </code></p><p>DSE Java 2.x</p><p><code>&lt;dependency&gt;   &lt;groupId&gt;com.datastax.dse&lt;/groupId&gt;   &lt;artifactId&gt;dse-java-driver-core&lt;/artifactId&gt;   &lt;version&gt;2.3.0&lt;/version&gt; &lt;/dependency&gt; </code></p><p>DSE Java 1.x</p><p><code>&lt;dependency&gt;   &lt;groupId&gt;com.datastax.dse&lt;/groupId&gt;   &lt;artifactId&gt;dse-java-driver-core&lt;/artifactId&gt;   &lt;version&gt;1.9.0&lt;/version&gt; &lt;/dependency&gt;</code></p><p><a href="https://docs.datastax.com/en/astra/aws/doc/dscloud/astra/dscloudMigrateJavaDriver.html">Source DataStax Drivers Docs</a></p><h2 id="what-if-i-can-t-upgrade">What if I can't upgrade?</h2><p>Driver upgrades are often just a simple dependency change but with frameworks like `lagom` and `akka-persistence-cassandra` that take care of session management and querying for you, you need the library to upgrade *it's* depentencies.</p><p>`lagom` depends on `akka-persistence-cassandra` which inherits the java driver version from `alpakka`.</p><p>As of the time of this writing (July 8, 2020) `alpakka` is updated to java driver 4.6.1 (which supports SCB), unfortunately this is only on the master branch today (Chris Batey pushed the dependency in this PR <a href="https://github.com/akka/alpakka/pull/2320">https://github.com/akka/alpakka/pull/2320</a>).</p><p>Until this makes it into a release and that release gets pulled by an `akka-persistence-cassandra` release and that gets pulled into a `lagom` release, we'll need a work around.</p><h2 id="workaround">Workaround</h2><p>I spent some time and figured out how to get current versions of `akka-persistence-cassandra` to connect to lagom.</p><h2 id="application-conf">application.conf</h2><pre><code># Configuration for akka-persistence-cassandra
akka.persistence.cassandra {
  events-by-tag {
    bucket-size = "Day"
    # for reduced latency
    eventual-consistency-delay = 200ms
    flush-interval = 50ms
    pubsub-notification = on
    first-time-bucket = "20200115T00:00"
  }

  query {
    refresh-interval = 2s
  }

  # don't use autocreate in production
  journal.keyspace-autocreate = off
  journal.tables-autocreate = on
  snapshot.keyspace-autocreate = off
  snapshot.tables-autocreate = on
  journal.keyspace = "&lt;ASTRA_KEYSPACE&gt;"
  snapshot.keyspace = "&lt;ASTRA_KEYSPACE&gt;"
}

datastax-java-driver {
  advanced.reconnect-on-init = on
  basic.contact-points = [ "&lt;ASTRA_DNS&gt;:&lt;ASTRA_CQL_PORT&gt;" ]
  basic.load-balancing-policy.local-datacenter = caas-dc
  local-datacenter = caas-dc
  advanced.ssl-engine-factory {
    class = DefaultSslEngineFactory


    hostname-validation = false

    truststore-path = ./&lt;PATH_TO_UNZIPPED_SCB&gt;/trustStore.jks
    truststore-password = &lt;TRUSTSTORE_PASSWORD&gt;
    keystore-path = ./&lt;PATH_TO_UNZIPPED_SCB&gt;/identity.jks
    keystore-password = &lt;KEYSTORE_PASSWORD&gt;
  }

  advanced.auth-provider {
    class = PlainTextAuthProvider
    username = &lt;ASTRA_C*_DB_USERNAME&gt;
    password = &lt;ASTRA_C*DB_PASSWORD&gt;
  }

}

akka.projection.cassandra.offset-store.keyspace = "&lt;ASTRA_KEYSPACE&gt;"
</code></pre><p>First, download your secure-connect-bundle from the Astra UI and unzip it in a directory in the host where you are running <code>akka-persistence-cassandra</code>.</p><p>Next, take a look at <code>config.json</code> and <code>cqlshrc</code>. You can pull the fields in &lt;&gt; in the config above from these two files:</p><pre><code>$ cat scb/config.json 
{
  "host": "&lt;ASTRA_DNS&gt;",
  "port": &lt;ASTRA_METADATA_PORT_THIS_IS_NOT_THE_CQL_PORT&gt;,
  "keyspace": "&lt;ASTRA_KEYSPACE&gt;",
  "localDC": "caas-dc",
  "caCertLocation": "./ca.crt",
  "keyLocation": "./key",
  "certLocation": "./cert",
  "keyStoreLocation": "./identity.jks",
  "keyStorePassword": "&lt;ASTRA_KEYSTORE_PASSWORD&gt;",
  "trustStoreLocation": "./trustStore.jks",
  "trustStorePassword": "&lt;ASTRA_TRUSTSTORE_PASSWORD&gt;",
  "csvLocation": "./data",
  "pfxCertPassword": "Lkl08BhEVq2e4bw6m"
}
</code></pre><pre><code>$ cat scb/cqlshrc 
[connection]
hostname = &lt;ASTRA_DNS&gt;
port = &lt;ASTRA_CQL_PORT&gt;
ssl = true

[ssl]
validate = true
certfile = ./ca.crt
userkey = ./key
usercert = ./cert
</code></pre><p>The config is kept in <a href="https://gist.github.com/phact/72d555dc248494faa8d91312a9cb753f">this gist</a> for your viewing and sharing pleasure and will be updated if things change.</p><h2 id="future">Future</h2><p>The DataStax team is working with the community to help get the driver version updated and documented. Stay tuned!</p>]]></content:encoded></item><item><title><![CDATA[DataStax Proxy for DynamoDB™ and Apache Cassandra™ - Preview]]></title><description><![CDATA[<p>Yesterday at ApacheCon, our very own Patrick McFadin announced the public preview of an open source tool that enables developers to run their AWS DynamoDB™ workloads on Apache Cassandra. With the DataStax Proxy for DynamoDB and Cassandra, developers can run DynamoDB workloads on premises, taking advantage of the hybrid, multi-model,</p>]]></description><link>https://www.sestevez.com/untitled/</link><guid isPermaLink="false">5d84fdaaa925bf188ff71384</guid><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Fri, 20 Sep 2019 16:32:33 GMT</pubDate><content:encoded><![CDATA[<p>Yesterday at ApacheCon, our very own Patrick McFadin announced the public preview of an open source tool that enables developers to run their AWS DynamoDB™ workloads on Apache Cassandra. With the DataStax Proxy for DynamoDB and Cassandra, developers can run DynamoDB workloads on premises, taking advantage of the hybrid, multi-model, and scalability benefits of Cassandra.</p><p>This post is cross posted on the <a href="https://www.datastax.com/blog/2019/09/datastax-proxy-dynamodb-and-apache-cassandra-preview">DataStax</a> <a href="https://www.datastax.com/blog/2019/09/datastax-proxy-dynamodb-and-apache-cassandra-preview">blog</a>.</p><h2 id="the-big-picture">The Big Picture</h2><p>Amazon DynamoDB is a key-value and document database which offers developers elasticity and a zero-ops cloud experience. However, the tight AWS integration that makes DynamoDB great for cloud is a barrier for customers that want to use it on premises.</p><p>Cassandra has always supported key-value and tabular data sets so supporting DynamoDB workloads just meant that DataStax customers needed a translation layer to their existing storage engine.</p><p>Today we are previewing a proxy that provides compatibility with the DynamoDB SDK, allowing existing applications to read/write data to DataStax Enterprise (DSE) or Cassandra without any code changes. It also provides the hybrid + multi-model + scalability benefits of Cassandra to DynamoDB users.</p><p><em>If you’re just here for the code you can find it in GitHub and DataStax Labs: <a href="https://github.com/datastax/dynamo-cassandra-proxy/">https://github.com/datastax/dynamo-cassandra-proxy/</a></em></p><h2 id="possible-scenarios">Possible Scenarios</h2><p><strong>Application Lifecycle Management:</strong> Many customers develop on premises and then deploy to the cloud for production. The proxy enables customers to run their existing DynamoDB applications using Cassandra clusters on-prem.</p><figure class="kg-card kg-image-card"><img src="https://www.datastax.com/sites/default/files/inline-images/DataStaxProxyOverview.JPG" class="kg-image" alt="DataStax Proxy Overview"></figure><p><strong>Hybrid Deployments:</strong> DynamoDB Streams can be used to enable hybrid workload management and transfers from DynamoDB cloud deployments to on-prem Cassandra-proxied deployments. This is supported in the current implementation and, like DynamoDB Global Tables, it uses DynamoDB Streams to move the data. For hybrid transfer to DynamoDB, check out the <a href="https://docs.datastax.com/en/dse/5.1/dse-admin/datastax_enterprise/config/configCDCLogging.html">Cassandra CDC improvements </a>which could be leveraged and stay tuned to the DataStax blog for updates on our Change Data Capture (CDC) capabilities.</p><figure class="kg-card kg-image-card"><img src="https://www.datastax.com/sites/default/files/inline-images/DataStaxProxySolArch.JPG" class="kg-image" alt="DataStax Proxy Architecture"></figure><h2 id="what-s-in-the-proxy"><strong>What’s in the Proxy?</strong></h2><p>The proxy is designed to enable users to back their DynamoDB applications with Cassandra. We determined that the best way to help users leverage this new tool and to help it flourish was to make it an open source Apache 2 licensed project.The code consists of a scalable proxy layer that sits between your app and the database. It provides compatibility with the DynamoDB SDK which allows existing DynamoDB applications to read and write data to Cassandra without application changes.</p><p><strong>How It Works</strong></p><p>A few design decisions were made when designing the proxy. As always, these are in line with the design principles that we use to guide development for both Cassandra and our DataStax Enterprise product.</p><p><strong>Why A Separate Process?</strong></p><p>We could have built this as a Cassandra plugin that would execute as part of the core process but we decided to build it as a separate process for the following reasons:</p><p>1) Ability to scale the proxy independently of Cassandra</p><p>2) Ability to leverage k8s / cloud-native toolingDeveloper agility and to attract contributors—developers can work on the proxy with limited knowledge of Cassandra internals</p><p>3) Independent release cadence, not tied to the Apache Cassandra project</p><p>4) Better AWS integration story for stateless apps (i.e., leverage CloudWatch alarm, autoscaling, etc.)</p><p><strong>Why Pluggable Persistence?</strong></p><p>On quick inspection, DynamoDB’s data model is quite simple. It consists of a hash key, a sort key, and a JSON structure which is referred to as an item. Depending on your goals, the DynamoDB data model can be persisted in Cassandra Query Language (CQL) in different ways. To allow for experimentation and pluggability, we have built the translation layer in a pluggable way that allows for different translators. We continue to build on this scaffolding to test out multiple data models and determine which are best suited for:</p><p>1) Different workloads</p><p>2) Different support for consistency / linearization requirements</p><p>3) Different performance tradeoffs based on SLAs</p><p><strong>Conclusion</strong></p><p>If you have any interest in running DynamoDB workloads on Cassandra, take a look at the project. Getting started is easy and spelled out in the readme and DynamoDB sections. Features supported by the proxy are quickly increasing and collaborators are welcome.</p><p><em><a href="https://github.com/datastax/dynamo-cassandra-proxy/">https://github.com/datastax/dynamo-cassandra-proxy/</a></em></p><p><em>All product and company names are trademarks or registered trademarks of their respective owner. Use of these trademarks does not imply any affiliation with or endorsement by the trademark owner.</em></p><hr><p><sup>1</sup>Often in the DynamoDB documentation, this key is referred to as a partition key, but since these are not one-to-one with DynamoDB partitions we will use the term hash key instead.</p>]]></content:encoded></item><item><title><![CDATA[Integrate Spark Metrics using DSE Insights Metrics Collector]]></title><description><![CDATA[<p>Metrics and visibility are critical when dealing with distributed systems.</p><p>In the case of DSE Analytics we are interested in monitoring the state of the various Spark processes (master, worker, driver, executor) in the cluster, the status of the work the cluster is doing (applications, jobs, stages, and tasks), and</p>]]></description><link>https://www.sestevez.com/grabbing-dse-analytics-spark-metrics-with-dse-insights-metrics-reporter-and-graphana-prometheus/</link><guid isPermaLink="false">5c7fd22aa925bf188ff7129c</guid><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Wed, 20 Mar 2019 18:28:42 GMT</pubDate><media:content url="http://www.sestevez.com/content/images/2019/03/prom.png" medium="image"/><content:encoded><![CDATA[<img src="http://www.sestevez.com/content/images/2019/03/prom.png" alt="Integrate Spark Metrics using DSE Insights Metrics Collector"><p>Metrics and visibility are critical when dealing with distributed systems.</p><p>In the case of DSE Analytics we are interested in monitoring the state of the various Spark processes (master, worker, driver, executor) in the cluster, the status of the work the cluster is doing (applications, jobs, stages, and tasks), and finally we are also interested in the detailed <a href="https://github.com/datastax/spark-cassandra-connector/blob/master/doc/11_metrics.md">metrics provided by the spark cassandra connector</a>. This article focuses on the first two and we leave the integration of the spark cassandra connector monitoring for a second post. </p><p>With the DataStax Enterprise (DSE) Metrics <a href="https://www.datastax.com/2018/12/improved-performance-diagnostics-with-datastax-metrics-collector">Collector</a> (new as of DSE 6.7 and backported to 6.0.5) DataStax makes exporting metrics to your monitoring solution of choice simple and easy. Donnie Robertson wrote an excellent DataStax Academy <a href="https://academy.datastax.com/content/dse-metrics-collector-tutorial-using-dse-docker-images">blog</a> on how to run DSE with the insights collector providing metrics for Prometheus and Grafana in a completely dockerized setup.</p><p>At its core, the DSE Metrics Collector is as a managed collectd sidecar bundled with the DSE binaries. DSE server manages the lifecycle of the collectd process and allows users to manage collectd configuration via `dsetool.`  Customers can ship the DSE metrics events (generated by the database) to the endpoint of their choice*.</p><p>To monitor DSE Analytics (spark jobs) we can leverage a collectd plugin* to monitor DSE Analytics / Spark.</p><h2 id="just-show-me-the-code-">Just show me the code!</h2><p>This <a href="https://github.com/phact/spark-insights/blob/master/.startup/spark-insights#L4">bash script</a>  stands up Prometheus &amp; Grafana and hooks up spark metrics assuming DSE is installed and running on localhost via a package install. I'll break down the steps in the rest of the article.</p><h2 id="collectd-spark">collectd-spark</h2><p>The lovely folks at Signalfx wrote a spark <a href="https://github.com/signalfx/collectd-sparkmkdir">plugin</a> for collectd that gathers metrics via HTTP from the spark master and worker. To use it, simply clone the plugin and move it to the dse collectd directory (in the case of a package install /usr/share/dse/collectd) as follows:</p><pre><code>git clone https://github.com/signalfx/collectd-spark

mkdir /usr/share/dse/collectd/collectd-spark
cp collectd-spark/spark_plugin.py /usr/share/dse/collectd/collectd-spark/
</code></pre>
<p>Since collectd-spark is written in python, we need to inform the bundled collectd where to find the python binaries with the following symlink:</p><pre><code># add config for collectd collectd spark plugin
ln -s /usr/lib/python2.7/ /usr/share/dse/collectd/usr/lib/python2.7</code></pre><p>We enable and configure both the collectd-spark plugin as well as the write Prometheus plugin by adding a config file in the DSE collectd directory. Notice that I dynamically pulled the spark master URL by hitting the Spark rest API running on localhost.</p><pre><code>MASTER_URL=$(curl localhost:7080 -LIs | grep Location | awk -F' ' '{print $2}' | awk -F':' '{print $1 &quot;:&quot; $2}')

mkdir /etc/dse/collectd/
cat &lt;&lt; EOF &gt; /etc/dse/collectd/10-spark.conf
LoadPlugin python
&lt;Plugin python&gt;
  ModulePath &quot;/tmp/spark-insights/collectd-spark&quot;

  Import spark_plugin

  &lt;Module spark_plugin&gt;
  MetricsURL &quot;$MASTER_URL&quot;
  MasterPort 7080
  WorkerPorts 7081
  Applications &quot;True&quot;
  Master &quot;$MASTER_URL:7080&quot;
  Cluster &quot;Standalone&quot;
  &lt;/Module&gt;
&lt;/Plugin&gt;

LoadPlugin write_prometheus
&lt;Plugin write_prometheus&gt;
 Port “9103”
&lt;/Plugin&gt;
EOF

</code></pre>
<h3 id="insights-collector">Insights Collector</h3><p>We are now ready to bring up collectd. If Metrics Collector is enabled and running, disable and enable it again or kill the collectd process. Killing the collectd process will trigger DSE to bring it back up with the new config.</p><pre><code># turn on collectd
# if insights has already been enabled, either DISABLE and then enable again or kill the collectd process. DSE will bring it back up with the new config.
#dsetool insights_config --mode DISABLE
dsetool insights_config --mode ENABLED_WITH_LOCAL_STORAGE</code></pre><h2 id="grafana-and-prometheus">Grafana and Prometheus</h2><p>Finally, we bring up Grafana and Prometheus using docker compose. If you already have Grafana and Prometheus running elsewhere, you can add the Prometheus targets to point to 9103 on each of your DSE nodes. Notice that we also clone the dse-metric-reporter dashboards from the DataStax repo that comes with pre-built Grafana dashboards for DSE.</p><p>The new Spark metrics will appear under collectd spark in Prometheus and Grafana allowing you to create custom dashboards for them. </p><pre><code>export PROMETHEUS_DATA_DIR=/mnt/ephemeral/prometheus
export GRAFANA_DATA_DIR=/mnt/ephemeral/grafana

mkdir $PROMETHEUS_DATA_DIR
mkdir $GRAFANA_DATA_DIR

chmod 777 $PROMETHEUS_DATA_DIR
chmod 777 $GRAFANA_DATA_DIR

git clone https://github.com/datastax/dse-metric-reporter-dashboards.git
cd dse-metric-reporter-dashboards

cat /etc/hosts | grep node | grep -v ext| grep -v allnodes | awk -F' ' '{print $1 &quot;:9103&quot;}'  | jq -R . | jq -s &quot;.| [{targets:[.[]], labels:{cluster: \&quot;test_cluster\&quot; }}]&quot; &gt; prometheus/tg_dse.json

pip install docker-compose

docker-compose up &amp;
</code></pre>
<p>The screenshot below shows Prometheus picking up data from three targets, only the first of which has been configured with the DSE Metrics Collector.</p><figure class="kg-card kg-image-card"><img src="http://www.sestevez.com/content/images/2019/03/image.png" class="kg-image" alt="Integrate Spark Metrics using DSE Insights Metrics Collector"></figure><p>The spark data can be visualized in the graph screen in Prometheus for Prometheus query troubleshooting:</p><figure class="kg-card kg-image-card"><img src="http://www.sestevez.com/content/images/2019/03/image-1.png" class="kg-image" alt="Integrate Spark Metrics using DSE Insights Metrics Collector"></figure><p>And in Grafana as well:</p><figure class="kg-card kg-image-card"><img src="http://www.sestevez.com/content/images/2019/03/image-2.png" class="kg-image" alt="Integrate Spark Metrics using DSE Insights Metrics Collector"></figure><p>Here's a sample dashboard I hope to contribute to the DataStax <a href="https://github.com/datastax/dse-metric-reporter-dashboards">Metrics Collector Github repo</a>. </p><figure class="kg-card kg-image-card"><img src="http://www.sestevez.com/content/images/2019/03/34.220.235.3_3000_d_XOWiQAqiz_new-dashboard-copy_panelId-47-orgId-1-refresh-5s-from-now-1h-to-now-tab-metrics.png" class="kg-image" alt="Integrate Spark Metrics using DSE Insights Metrics Collector"><figcaption>Spark Dashboard</figcaption></figure><p>Hope you have found this article useful. Happy monitoring!</p><h1 id="resources">Resources</h1><p><a href="https://github.com/datastax/dse-metric-reporter-dashboards">https://github.com/datastax/dse-metric-reporter-dashboards</a></p><p><a href="https://github.com/signalfx/integrations/tree/master/collectd-spark#configuration">https://github.com/signalfx/integrations/tree/master/collectd-spark#configuration</a></p><p><a href="https://github.com/signalfx/collectd-spark/tree/v1.0.2/integration-test">https://github.com/signalfx/collectd-spark/tree/v1.0.2/integration-test</a></p><p><a href="https://academy.datastax.com/content/dse-metrics-collector-tutorial-using-dse-docker-images">https://academy.datastax.com/content/dse-metrics-collector-tutorial-using-dse-docker-images</a></p><p>* Collectd supports most monitoring systems via<a href="https://collectd.org/wiki/index.php/Category:Callback_write"> collectd write plugins</a>.</p>]]></content:encoded></item><item><title><![CDATA[DSE Gremlin Queries: Good, Better, Best]]></title><description><![CDATA[<h2 id="whysomegremlinqueriesrunfasterthanothers">Why Some Gremlin Queries Run Faster than Others</h2>
<h3 id="intro">Intro</h3>
<blockquote>
<p>With great power comes great responsibility.</p>
</blockquote>
<blockquote>
<p><em>--Spiderman's Uncle</em></p>
</blockquote>
<p>The Gremlin language gives users great power in the form of traversal expressivity. With the <a href="http://tinkerpop.apache.org/docs/current/reference/#graph-traversal-steps">dozens of steps</a> available in the Gremlin language, developers can make their little Gremlin(s) jump around the</p>]]></description><link>https://www.sestevez.com/dse-gremlin-queries-good-better-best/</link><guid isPermaLink="false">5a56690547d72f4798d9e493</guid><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Wed, 04 Oct 2017 21:00:59 GMT</pubDate><content:encoded><![CDATA[<h2 id="whysomegremlinqueriesrunfasterthanothers">Why Some Gremlin Queries Run Faster than Others</h2>
<h3 id="intro">Intro</h3>
<blockquote>
<p>With great power comes great responsibility.</p>
</blockquote>
<blockquote>
<p><em>--Spiderman's Uncle</em></p>
</blockquote>
<p>The Gremlin language gives users great power in the form of traversal expressivity. With the <a href="http://tinkerpop.apache.org/docs/current/reference/#graph-traversal-steps">dozens of steps</a> available in the Gremlin language, developers can make their little Gremlin(s) jump around the graph every which way they like. However, not all paths that reach the same result are equal.</p>
<p>This article discusses Gremlin query optimization for DSE Graph. We will walk through examples of queries that just do the job, and improve upon them to get better performance while focusing on the reasoning and introspection tools required to identify bottlenecks and measure improvements. With this guidance, readers can reproduce comparable query tuning results in their own environments and achieve performant real-time transactional Gremlin queries.</p>
<p><strong>Note:</strong> For the purposes of this article, a good query is one that gets the expected result. These &quot;good&quot; queries will be inefficient but we use them to highlight common mistakes and the techniques used to avoid them.</p>
<h3 id="oneindexatatime">One Index at a time</h3>
<p>DSE Graph queries use one index at a time. When designing a graph query, be conscious of the fact that DSE Graph’s current implementation allows it to take advantage of one index per query. Once that index is used up, the remaining execution will happen in memory at the DSE Graph server in the DSE JVM.</p>
<h4 id="grabtherightindex">Grab the right index</h4>
<p>Take the following <strong>&quot;good&quot;</strong> query:</p>
<pre><code>g.V().has('state','name', 'california').
  in('lives_in').
  hasLabel('person').
  has('first_name', regex('Seb.+')).
  has('last_name', regex('Est.+'))
</code></pre>
<p>In the query above, we are performing regex matches on <code>first_name</code> and <code>last_name</code> (properties on the <code>person</code> vertex) and an exact match on the <code>state</code> <code>name</code> california. Since we are able to leverage one search index (the index on the <code>state</code> vertex or the index on the <code>person</code> vertex) we should consider which filter will return fewer values and perform that filter first.</p>
<p>For example, if we are expecting just a few individuals with names starting with Seb / Est in the dataset, would make sense to write a <strong>better</strong> query as follows:</p>
<pre><code>g.V().hasLabel('person').  
  has('first_name', regex('Seb.+')).
  has('last_name', regex('Est.+')).as('people').
  out('lives_in').
  has('state','name', 'california').
  select('people')
</code></pre>
<p>Notice the use of the <code>as</code> / <code>select</code> steps to ensure that our result set still contains <code>person</code> vertices and not <code>state</code> vertices. Using  as / select enables path tracking, which can be computationally expensive so an even better way (<strong>best</strong>) to write this query would be as follows:</p>
<pre><code>g.V().hasLabel('person').  
  has('first_name', regex('Seb.+')).
  has('last_name', regex('Est.+')).
  where(
    out('lives_in').
    has('state','name', 'california')
)
</code></pre>
<p>The where step (subfilter) allows us to return the relevant person vertices without enabling path tracking.</p>
<p>Another way to tackle this 'order of traversal problem' is to leverage the Gremlin <a href="http://tinkerpop.apache.org/docs/current/reference/#match-step">match step</a>.</p>
<h4 id="splittingqueries">Splitting queries</h4>
<p><strong>Note:</strong> The following is one of the cases where as the DSE Graph optimizer improves; the difference between good, better, and best queries vanishes. The .or condition will combine the constraints into a single search query in the latest DSE so there is no need to manually optimize!</p>
<p>Sometimes which filter is more selective is not obvious and it makes sense to perform two queries instead of one.</p>
<p>In versions of DSE older than 5.1.3, this is true when an .or condition is present.</p>
<p>In the following <strong>&quot;good&quot;</strong> query, we have multiple filters (based off of different vertices) but DSE Graph can only execute the query in scan mode (There is a ticket to fix .or handling so this is only a near term problem).</p>
<pre><code>g.V().  
  or(
    out('lives_in').has('name', 'california'),
    has('person','name', regex('Seb.+')),
    has('person', 'company', 'DataStax')
  ).
  values('first_name')
</code></pre>
<p>Another (<strong>better</strong>) way to obtain this result set would be to run two separate queries and glue them together app side:</p>
<pre><code>g.V().has('state','name','california').
  in('lives_in').values('first_name')

g.V().has('person','first_name', regex('Seb.+')).
  has('company', 'DataStax').
  values('first_name')
</code></pre>
<p>Notice only two queries (not three) are necessary, because the <code>first name</code> and the <code>works for</code> filters can leverage the same search index on the person vertex.</p>
<p>We can also run the following Groovy statements to obtain the result set in a single call to the database (<strong>best</strong>).</p>
<pre><code>def names = g.V().has('state','name','california').
  in('lives_in').values('first_name').toSet()


names += g.V().has('person','first_name', regex('Seb.+')).
  has('company', 'DataStax').
  values('first_name').toSet()
</code></pre>
<h3 id="pagingingremlin">Paging in Gremlin</h3>
<p>For queries that expect large result sets paging is required. We can accomplish paging by using the .range step. However, the <code>.range</code> step’s latency will degrade with the max value in the return parameters (not the number of values being returned). This is a <strong>&quot;good&quot;</strong> query in that it will eventually get you the results you need (as long as it doesn't time out!):</p>
<pre><code>g.V().has('person', 'company', 'DataStax').
  where(out('lives_in').has('name','california')).
  has('first_name', regex('S.+')).
  range(0,1).values('first_name')
</code></pre>
<p>In the query above, we read 1010 values from DSE and throw 1000 of them away in-memory at the Graph server.<br>
As an (<strong>best</strong>) alternative to .range for paging, we can use a search index on the person_id field for paging with DSE Search.</p>
<p><strong>Note:</strong> We chose <code>person_id</code> because we needed a sortable and unique field we could use to page against. <code>person_id</code> is an integer and integers in DSE Search are indexed as Lucene TrieIntFields. <a href="https://docs.datastax.com/en/dse/5.1/dse-dev/datastax_enterprise/search/solrTypeMapping.html">Trie</a> fields in Lucene leverage the <a href="https://en.wikipedia.org/wiki/Trie">trie data structure</a> to allow range filtering and sorting.</p>
<p>In your app, hang on to the max value from the previous page:</p>
<pre><code>def max = 1000

def nameSet = g.V().has('person', 'company', 'DataStax').
  where(out('lives_in').has('name', 'california')).
  has('first_name', regex('S.+')).values('person_id').toSet()


g.V().has('person','person_id', within(nameSet)).
  has('person_id', gt(max)).
  order().by('person_id').range(0,10)
</code></pre>
<p><strong>Note:</strong> The Gremlin <a href="http://tinkerpop.apache.org/docs/current/reference/#timelimit-step">timeLimit</a> steps allows you to set a limit on the time the Gremlin server will spend evaluating a request. When troubleshooting queries where the result set may be large, the timeLimit step is a great way to speed up your itterations.</p>
<h3 id="conclusion">Conclusion</h3>
<p>With Gremlin, DSE Graph provides a powerful interaction mechanism capable of turning graph data into insights. Having come up with queries that work, developers can spend time optimizing them to ensure performance that falls within their SLAs.</p>
<p>Remember that as the optimizer in DSE Graph matures, some of these optimizations will start to happen on their own and the difference between a good, a better, and a best Gremlin query will shrink and may eventually disappear.</p>
]]></content:encoded></item><item><title><![CDATA[Large Graph Loading Best Practices: Tactics (Part 2)]]></title><description><![CDATA[<p>The <a href="http://www.sestevez.com/large-graph-strategies-part-1/">previous post</a> introduced DSE Graph and summarized some key considerations related to dealing with large graphs. This post aims to:</p>
<ol>
<li>describe the tooling available to load large data sets to DSE Graph</li>
</ol>
<ul>
<li>point out tips, tricks, and key learnings for bulk loading with DSE Graph Loader</li>
<li>provide code samples</li></ul>]]></description><link>https://www.sestevez.com/large-graph-tactics-part-2/</link><guid isPermaLink="false">5a56690547d72f4798d9e492</guid><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Tue, 14 Mar 2017 13:55:51 GMT</pubDate><content:encoded><![CDATA[<p>The <a href="http://www.sestevez.com/large-graph-strategies-part-1/">previous post</a> introduced DSE Graph and summarized some key considerations related to dealing with large graphs. This post aims to:</p>
<ol>
<li>describe the tooling available to load large data sets to DSE Graph</li>
</ol>
<ul>
<li>point out tips, tricks, and key learnings for bulk loading with DSE Graph Loader</li>
<li>provide code samples to simplify your bulk loading process into DSE Graph</li>
</ul>
<h3 id="tooling">Tooling</h3>
<p>DSE Graph Loader (DGL) is a powerful tool for loading graph data into DSE Graph. As shown in the marchitecture diagram below, the DGL supports multiple data input sources for bulk loading and provides high flexibility for manipulating data on ingest by requiring custom groovy data mapping scripts to map source data to graph objects. See the <a href="http://docs.datastax.com/en/latest-dse/datastax_enterprise/graph/dgl/graphloaderTOC.html">DataStax docs</a> which cover the DGL, it's API, and DGL mapping scripts in detail.</p>
<p><img src="http://www.sestevez.com/sestevez/img/dgl.png" alt="landscape"></p>
<p>This article breaks down the tactics for efficient data loading with DGL into the following areas:</p>
<ul>
<li>file processing best practices</li>
<li>mapping script best practices</li>
<li>DGL configuration</li>
</ul>
<h3 id="codeandtactics">Code and Tactics</h3>
<p>Code to accompany this section can be found at:</p>
<p><a href="https://github.com/phact/rundgl">https://github.com/phact/rundgl</a></p>
<p><em>Shout out to Daniel Kuppitz, Pierre Laporte, Caroline George, Ulisses Beresi, Bryn Cooke among others who helped create and refine this framework. Any bugs / mistakes are mine.</em></p>
<p>The code repository consists of:</p>
<ul>
<li>a wrapper bash script that is used for bookkeeping and calling DGL</li>
<li>a mapping script whith some helpful utilities and * a generic structure that can be used as a starting point for your own custom mapping scripts</li>
<li>a set of analysis scripts that can be used to monitor your load</li>
</ul>
<p>The rest of this article will be focused on DGL loading best practices linking to specific code in the repo for clarity.</p>
<h4 id="filebucketing">File Bucketing</h4>
<p>The main consideration to take when loading large data volumes is that DGL performance will suffer if fed too much data in a single run. At the time of this article (2/28/2017), the DGL has features designed for non-idempotent graphs (including deduplication of vertices via an internal vertex cache) that limit its performance with large idempotent graphs.</p>
<p>Splitting your loading files into chunks of about ~120Million or fewer vertices will ensure that the DGL does not lock up when the vertex cache is saturated.</p>
<p><strong>UPDATE:</strong> In modern versions of the graph loader, custom vertices are no longer cached. This means there should not be a performance impact of large files for this kind of load. Chunking may still be useful for percentage tracking / restarting / etc.</p>
<p>The rundgl script found in the repo is designed to traverse a directory and bucket files, feeding them to DGL a bucket at a time to maximize performance.</p>
<h4 id="trackyourprogress">Track your progress</h4>
<p>The analysis directory in the rundgl repo contains a script called chart. This script will aggregate statistics from the DGL loader.log and generate throughput statistics and charts for the different loading phases that have occurred (Vertices, Edges, and Properties).</p>
<p><em><strong>Note</strong></em> <em>- these scripts have only been tested with DGL &lt; 5.0.6</em></p>
<p>Navigate to the analysis directory and run <code>./charts</code> to get a dump of the throughput for your job:</p>
<p><img src="http://www.sestevez.com/sestevez/img/dgl_results.png" alt="analysis"></p>
<p>It will also start a simple http server in the directory for easy access to the png charts it generates, here is an example chart output:</p>
<p><img src="http://www.sestevez.com/sestevez/img/total-throughput.png" alt="total throughput"></p>
<p><em>Thank you Pierre for building and sharing the analysis scripts</em></p>
<h4 id="monitordglerrors">Monitor DGL errors</h4>
<p>When the DGL gets timeouts from the server it does not log them to STDOUT, they can only be seen in logger.log. In a busy system, it is normal to see timeouts. These timeouts will be handled by retry policies which are baked into the loader. Too many timeouts may be a sign that you are overwhelming your cluster and need to either slow down (reduce threads in the loader) or scale out (add nodes in DSE). You will know if timeouts are affecting your cluster if your overall throughput starts trending down or if you are seeing backed up threadpools or dropped mutations in OpsCenter.</p>
<p>Aside from Timeouts, you may also see errors in the DGL log that are caused by bad data or bugs in your mapping script. If enough of these errors happen the job will stop. To avoid having to restart from the beginning on data related issues, take a look at the bookeeping section below.</p>
<h4 id="dontuses3">Don't use S3</h4>
<p>If you are looking to load significant amounts of data, do not use S3 as a source for performance reasons. It will take less time to parallel rsync your data from s3 to a local SSD and then load it than to load directly from s3.</p>
<p>DGL does have S3 hooks and from a functionality perspective it works quite well (including aws authentication) so if you are not in a hurry, the repo also includes an example for pulling data from S3. Just be aware of the performance overhead.</p>
<h4 id="groovymapperbestpractices">Groovy mapper best practices</h4>
<p>Custom groovy mapping scripts can be error prone and the DGL's error messages sometimes leave a bit to be desired. The framework provided aims to simplify the loading process by providing a standard framework for all your mapping scripts and minimizing the amount of logic that goes into the mapping scripts.</p>
<h4 id="usethelogger">Use the logger</h4>
<p>DGL mapping scripts can use the log4j logger to log messages. This can be very useful when troubleshooting issues in a load, especially issues that only show up at runtime with a particular file.</p>
<p>It also allows you to track when particular loading events occur during execution.</p>
<p>INFO, WARN, and ERROR messages will be logged to logger.log and will include a timestamp.</p>
<h4 id="dgltakesarguments">DGL takes arguments</h4>
<p>If you need to pass an argument to the groovy mapper just pass it with <code>- &lt;argname&gt;</code> in the command line.<br>
The variable argname will be available in your mapping script.</p>
<p>For example, ./rundgl passes <code>-inputfilename</code> to DGL <a href="https://github.com/phact/rundgl/commit/6d008c86e98403c001c4164fcd8772ee9c2fa9bd#diff-ee4a3dcdb94cf3b56f6b0f191b4d4fd9R51">here</a> leveraging this feature. You can see the mapping script use it <a href="https://github.com/phact/rundgl/commit/6d008c86e98403c001c4164fcd8772ee9c2fa9bd#diff-46c8d2f865ad43987bc511d246a4b159R101">here</a>.</p>
<p>The argument <code>inputfilename</code> is a list of files to process to the DGL. This helps us avoid having to do complex directory traversals in the mapping script itself.</p>
<p>By traversing files in the wrapper script, we are also able to do some bookeeping.</p>
<h4 id="bookeeping">Bookeeping</h4>
<p><code>loadedfiles.txt</code> tracks the start and end of your job as well as the list of files that were loaded and when each particular load completed. This also enables us to &quot;resume&quot; progress by modifying the <code>STARTBUCKET</code>.</p>
<p><code>STARTBUCKET</code> represents the first bucket that will be processed by <code>./rundgl</code>, if you stopped your job and want to continue where you left off, count the number of files in <code>loadedfiles.txt</code> and divide by the <code>BUCKETSIZE</code>, this will give you the bucket you were on. Starting from that bucket will ensure you don't miss any files. Since we are working with idempotent graphs, we don't have to worry about duplicates etc.</p>
<h4 id="dglvertexcachetempdirectory">DGL Vertex Cache Temp Directory</h4>
<p>Especially when using EBS, make sure to move the default directory of the DGL Vertex Cache temp directory.</p>
<p>IO contention against the mapdb files used by DGL for its internal vertex cache will overwhelm amazon instances' root EBS partitions</p>
<p><code>Djava.io.tmpdir=/data/tmp</code></p>
<h4 id="leavesearchforlast">Leave search for last</h4>
<p>If you are working on a graph schema that uses DSE Search indexes, you can optimize for overall load + indexing time by loading the data without the search indexes first and then creating the indexes once the data is in the system.</p>
<h3 id="somescreenshots">Some screenshots</h3>
<p>Here are some screenshots from a system using this method to load billons of vertices. The spikes are each run of the DGL kicked off in sequence by the rundgl mapping script. You can see the load and performance are steady throughout the load giving us dependable throughput.<br>
Like with other DSE workloads, if you need more speed, scale out!</p>
<h4 id="throughput">Throughput:</h4>
<p><img src="http://www.sestevez.com/sestevez/img/long_throughput.png" alt="throughput"></p>
<h4 id="osload">OS Load:</h4>
<p><img src="http://www.sestevez.com/sestevez/img/os-load.png" alt="osload"></p>
<h3 id="conclusion">Conclusion</h3>
<p>With the tooling, code, and tactics in this article you should be ready to load billions of V's, E's, and P's into DSE graph. The <code>./rundgl</code> repo is there to help with error handling, logging, bookeeping, and file bucketing so that your loading experience is smooth. Enjoy!</p>
]]></content:encoded></item><item><title><![CDATA[Large Graph Loading Best Practices: Strategies (Part 1)]]></title><description><![CDATA[<p>This post is an intro to DSE Graph with a focus on the strategies that should be used to load large graphs with billions of vertices and edges.  For those familiar with DSE Graph and large graph strategies or those who want to dive directly into loading data, proceed to</p>]]></description><link>https://www.sestevez.com/large-graph-strategies-part-1/</link><guid isPermaLink="false">5a56690547d72f4798d9e491</guid><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Tue, 14 Mar 2017 13:55:09 GMT</pubDate><content:encoded><![CDATA[<p>This post is an intro to DSE Graph with a focus on the strategies that should be used to load large graphs with billions of vertices and edges.  For those familiar with DSE Graph and large graph strategies or those who want to dive directly into loading data, proceed to the next post in this two part series entitiled <a href="http://www.sestevez.com/large-graph-tactics-part-2/">Large Graph Loading Best Practices: Tactics</a>.</p>
<h3 id="introtodsegraph">Intro to DSE Graph</h3>
<p>DSE Graph is differentiated from other graph databases by building on DataStax Enterprise's scalability, replication, and fault tolerance.</p>
<p><em><strong>Note</strong> - To understand how DSE Graph data is stored in DSE's Apache Cassandra(TM) storage engine, check out <a href="https://www.datastax.com/dev/blog/a-letter-regarding-native-graph-databases">Matthias</a> and <a href="https://www.datastax.com/dev/blog/scalable-graph-computing-der-gekrummte-graph">Marko</a>'s posts on the matter.</em></p>
<p>When folks ask me where DSE Graph falls in the greater database / graph database landscape, I use this image to communicate the combination of scalability and value in relationships that make DSE Graph such a unique product:</p>
<p><img src="http://www.sestevez.com/sestevez/img/graph_landscape.png" alt="landscape"></p>
<p>DSE Graph is positioned on the right side of the chart where relationships are most valuable, and toward the top of the chart due to the scalability it inherits from DSE and Cassandra. The third key aspect that differentiates DSE graph is the velocity of the data it can support. Unlike analytical graph engines which load a static graph to memory and then crunch that static graph for insights, an operational graph is constantly changing as the real world concepts whose relationships and vertices it represents are created, updated, and deleted.</p>
<p><em><strong>Key Takeaway</strong> - DSE Graph is designed as a real-time, operational, distributed graph database.</em></p>
<h3 id="motivationandgoalsplayingwithscalablegraphs">Motivation and Goals: Playing with Scalable Graphs</h3>
<p>If you have  distributed graph problem, you may want to bulk load your data into DSE graph and start querying. However, loading significant amounts of data (&gt;1 billion V's or E's) into graph dbs is a time consuming, nontrivial task. The purpose of this article is to summarize some key design considerations related to dealing with large graphs.</p>
<h3 id="largegraphsidempotenceandscalability">Large graphs, idempotence, and scalability</h3>
<p>Idempotence is a common concept in distributed systems design. If an operation is idempotent, it can be repeated over and over and still yield the same result. We use idempotence to help us solve problems like the fact that <a href="http://bravenewgeek.com/you-cannot-have-exactly-once-delivery/">exactly once delivery does not exist</a>, it also greatly simplifies the design of our systems, minimizing bugs and promoting maintainablility. For the purposes of two part article series, we are going to focus on building scalable distributed idempotent graphs. This is one of the design choices that is supported in DSE Graph but note that not all graphs that can be built on DSE graph will have idempotent vertices and idempotent edges.</p>
<h4 id="idempotentvertices">Idempotent Vertices</h4>
<p>DSE graph allows two types of vertices, 1) those with system generated keys and 2) those with custom ids. For the purposes of this two part series we are going to concentrate on custom ids. Custom ids are useful for large graph problems in that they allow developers to take graph partitioning into their own hands. Custom ids will feel familiar if you have used DSE or cassandra and understand data modeling.</p>
<p>You configure the partition key of your Vertex label with a DDL operation:</p>
<pre><code>schema().vertexLabel('MachineSensor').partitionKey('manufacturing_plant_id').clusteringKey('sensor_id').create()
</code></pre>
<p>If you are using custom ids, the partition key is required and the clustering key is optional. For more on Cassandra data modeling and  clustering keys vs. partition keys see my post on <a href="https://www.sestevez.com/large-graph-strategies-part-1/www.sestevez.com/data-modeler/">data modeling for DSE</a>.</p>
<p><em><strong>Note</strong></em> <em>- your partition key, clustering key combination should provide uniqueness for the vertex. With this configuration, reinserting will not generate duplicates</em></p>
<h4 id="idempotentedges">Idempotent Edges</h4>
<p>DSE Graph edges support different cardinality options. For multiple cardinality edges (where there can be more than one edge between the same two vertices of the same edge label type) edge creation is <strong>not</strong> idempotent.</p>
<p>For the purposes of this two part article series, we will focus on single cardinality (thereby idempotent) edges. You can create single cardinality edge lables in DSE Graph use the <code>single()</code> keyword:</p>
<pre><code>schema.edgeLabel('has_sensor').single().create()
</code></pre>
<h3 id="letsload">Let's load!</h3>
<p>Having considered the strategies mentioned above, let's proceed to the <a href="http://www.sestevez.com/large-graph-tactics-part-2/">second part</a> which adresses the tactical aspects of loading large graphs.</p>
]]></content:encoded></item><item><title><![CDATA[Cluster Migration - Keeping simple things simple]]></title><description><![CDATA[<p>I often get asked about the different ways to move data across DSE clusters (prod to qa, old cluster to new cluster, multi-cluster ETL). There are different options for these ranging from custom apps, cassandra-loader / unloader (which I've talked about in another post), and Apache Spark (TM).</p>
<p>Out of these</p>]]></description><link>https://www.sestevez.com/cluster-migration-keeping-simple-things-simple/</link><guid isPermaLink="false">5a56690547d72f4798d9e490</guid><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Mon, 07 Nov 2016 18:48:27 GMT</pubDate><content:encoded><![CDATA[<p>I often get asked about the different ways to move data across DSE clusters (prod to qa, old cluster to new cluster, multi-cluster ETL). There are different options for these ranging from custom apps, cassandra-loader / unloader (which I've talked about in another post), and Apache Spark (TM).</p>
<p>Out of these options spark is the most scalable and performant option, but is also the most intimidating for a new user. Fortunately, DataStax Enterprise (DSE) makes Apache Spark (TM) integration with Apache Cassandra (TM), <a href="http://docs.datastax.com/en/latest-dse/datastax_enterprise/spark/startingSpark.html?hl=spark_enabled">trivial</a>. Additionally the <a href="https://github.com/datastax/spark-cassandra-connector">spark-cassandra-connector</a> is mature and user friendly so writing the code required for a cluster migration in scala is also trivial.</p>
<p>Unfortunately, the mention of learning how to program a new compute framework and setting up SBT to build a scala job can be daunting and sometimes keeps folks from exploring this avenue.</p>
<p><a href="https://github.com/phact/dse-cluster-migration">Here</a> is a pre-built spark job, that you can simply run against your clusters to perform a spark powered migration.</p>
<p>Spark has to be <a href="http://docs.datastax.com/en/latest-dse/datastax_enterprise/spark/startingSpark.html?hl=spark_enabled">enabled</a> on one of the clusters. SSH into that cluster and run:</p>
<pre><code>wget https://github.com/phact/dse-cluster-migration/releases/download/v0.01/dse-cluster-migration_2.10-0.1.jar

dse [-u &lt;usrer&gt; -p &lt;password&gt;] spark-submit --class phact.MigrateTable --conf spark.dse.cluster.migration.fromClusterHost='&lt;from host&gt;' --conf spark.dse.cluster.migration.toClusterHost='&lt;to host&gt;' --conf spark.dse.cluster.migration.keyspace='&lt;keyspace&gt;' --conf spark.dse.cluster.migration.table='&lt;table&gt;' --conf spark.dse.cluster.migration.newtableflag='&lt;true | false&gt;' --conf spark.dse.cluster.migration.fromuser='&lt;username&gt;' --conf spark.dse.cluster.migration.frompassword='&lt;password&gt;' --conf spark.dse.cluster.migration.touser='&lt;username&gt;' --conf spark.dse.cluster.migration.topassword='&lt;password&gt;' ./dse-cluster-migration_2.10-0.1.jar
</code></pre>
<p><strong>Update:</strong> I added username / password and table creation to the migration app.</p>
<p>Shout out to <a href="https://twitter.com/russspitzer">Russ</a> and <a href="https://twitter.com/brianmhess">Brian</a> for their help.</p>
<p>For the deeper dive on how this code works check out <a href="http://www.russellspitzer.com/2016/01/14/Multiple-Clusters-Spark-Cassandra/">Russ's post</a>.</p>
]]></content:encoded></item><item><title><![CDATA[C* schema changes and compatible types]]></title><description><![CDATA[<p>All the schema operations that can be done in c* are done without downtime. You should limit these actions as a best practice to 1 client (not multiple concurrent clients) to avoid schema disagreement problems.</p>
<p>The schema changes that are allowed are as follows (and documented here):</p>
<pre><code>cqlsh&gt; help</code></pre>]]></description><link>https://www.sestevez.com/c-compatible-types/</link><guid isPermaLink="false">5a56690547d72f4798d9e48f</guid><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Tue, 31 May 2016 16:47:14 GMT</pubDate><content:encoded><![CDATA[<p>All the schema operations that can be done in c* are done without downtime. You should limit these actions as a best practice to 1 client (not multiple concurrent clients) to avoid schema disagreement problems.</p>
<p>The schema changes that are allowed are as follows (and documented here):</p>
<pre><code>cqlsh&gt; help 
CQL help topics:
================
ALTER                        CREATE_TABLE_OPTIONS  SELECT
ALTER_ADD                    CREATE_TABLE_TYPES    SELECT_COLUMNFAMILY
ALTER_ALTER                  CREATE_USER           SELECT_EXPR
ALTER_DROP                   DELETE                SELECT_LIMIT
ALTER_RENAME                 DELETE_COLUMNS        SELECT_TABLE
ALTER_USER                   DELETE_USING          SELECT_WHERE
ALTER_WITH                   DELETE_WHERE          TEXT_OUTPUT
APPLY                        DROP                  TIMESTAMP_INPUT
ASCII_OUTPUT                 DROP_COLUMNFAMILY     TIMESTAMP_OUTPUT
BEGIN                        DROP_INDEX            TRUNCATE
BLOB_INPUT                   DROP_KEYSPACE         TYPES
BOOLEAN_INPUT                DROP_TABLE            UPDATE
COMPOUND_PRIMARY_KEYS        DROP_USER             UPDATE_COUNTERS
CREATE                       GRANT                 UPDATE_SET
CREATE_COLUMNFAMILY          INSERT                UPDATE_USING
CREATE_COLUMNFAMILY_OPTIONS  LIST                  UPDATE_WHERE
CREATE_COLUMNFAMILY_TYPES    LIST_PERMISSIONS      USE
CREATE_INDEX                 LIST_USERS            UUID_INPUT
CREATE_KEYSPACE              PERMISSIONS
CREATE_TABLE                 REVOKE
</code></pre>
<p>For regular columns and partition keys:</p>
<p>Compatible data types are as follows ( &lt;--&gt; denotes both way compatibility ; --&gt; denotes one way compatibility)):<br>
int --&gt; varint<br>
varchar &lt;--&gt; text<br>
int --&gt; blob<br>
text --&gt; blob<br>
ascii --&gt; blob<br>
double --&gt;blob<br>
int --&gt; blob<br>
timeuuid --&gt; blob<br>
varchar --&gt; blob<br>
bigint --&gt; blob<br>
boolean --&gt; blob<br>
decimal --&gt; blob<br>
float --&gt; blob<br>
inet --&gt; blob<br>
ltimestamp --&gt; blob<br>
uuid --&gt; blob<br>
varint --&gt; blob<br>
timeuuid --&gt; uuid</p>
<p>For clustering columns:<br>
int --&gt; varint<br>
varchar &lt;--&gt; text</p>
<p>The reason clustering columns are different is because they must also be order-compatible (clustering columns mandate the order in which we lay out data on disk, hence the stricter requirement).</p>
<p>As you can see, for the most part, CQL3 is relatively strict when it comes to type changes and you want to make sure you pick the right types at design time. You can always create and delete columns if you need to chage due to some unforseen circumstances. We do have tools in DSE Analytics to help with these operational changes when needed.</p>
]]></content:encoded></item><item><title><![CDATA[On Cassandra Collections, Updates, and Tombstones]]></title><description><![CDATA[<h3 id="update">update</h3>
<p>I was chatting with a user today who referenced this old post. Most of it is still relevant but <code>sstable2json</code> is no longer supported in modern c*. The new tool is <code>sstabledump</code>. The two tools are pretty much equivalent so you can just replace <code>sstabe2json</code> with <code>sstabledump</code> everywhere you</p>]]></description><link>https://www.sestevez.com/on-cassandra-collections-updates-and-tombstones/</link><guid isPermaLink="false">5a56690547d72f4798d9e48d</guid><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Thu, 26 May 2016 00:54:52 GMT</pubDate><content:encoded><![CDATA[<h3 id="update">update</h3>
<p>I was chatting with a user today who referenced this old post. Most of it is still relevant but <code>sstable2json</code> is no longer supported in modern c*. The new tool is <code>sstabledump</code>. The two tools are pretty much equivalent so you can just replace <code>sstabe2json</code> with <code>sstabledump</code> everywhere you see it here the outputs may have slightly different formatting but it should not matter in substance.</p>
<h3 id="cassandracollectionscreatetombstones">Cassandra collections create tombstones?</h3>
<p>Many new cassandra users learn this the hard way, they choose cassandra collections for the wrong reasons, for the wrong use cases, and then experience what is known as death by tombstones.</p>
<p>Update - To hear Luke Tillman, Patrick McFadin, and Eric Stevens talking about this post check out this video on Planet Cassandra! <a href="https://t.co/n9a6RFP5mP">https://t.co/n9a6RFP5mP</a></p>
<h3 id="tldr">TL;DR</h3>
<p>When folks ask me if they should use collections here are my recommendations.</p>
<h4 id="whydocassandradeveloperschoosecollections">Why do cassandra developers choose collections?</h4>
<h5 id="relationalmindset">Relational mindset:</h5>
<ol>
<li>It feels more natural--warm and fuzzy--to model one to many relationships if you don’t have to de-normalize tables (this is a very common reason, but not a great reason).</li>
</ol>
<h5 id="convenientreads">Convenient reads:</h5>
<ol start="2">
<li>
<p>Need to get a nested java structure directly out of the query</p>
<p>SELECT entitlements from entitlements_by_user WHERE … ;</p>
</li>
<li>
<p>Access whole collection or parts of the collection based on query patterns:</p>
</li>
</ol>
<pre><code>SELECT * FROM entitlements_by_user WHERE entitlements CONTAINS ‘App ABC';
</code></pre>
<h5 id="convenientwrites">Convenient writes:</h5>
<p>Ability to do incremental updates or deletes :</p>
<pre><code>UPDATE entitlements_by_user ... entitlements= entitlements + ‘App ABC’
</code></pre>
<p>This convenience does not come free:</p>
<ul>
<li>Serialization &amp; deserialization takes time with maps due to the complex java objects</li>
<li>(non incremental) inserts/updates on Maps generate tombstones. Insert/Update heavy workloads are not collection friendly. Excessive tombstones significantly affect compaction performance.</li>
<li>Collections are not designed to hold more than 10’s of fields. Compactions and repairs will be slow if you abuse collections.</li>
</ul>
<p>**Therefore -- Ensure you have a good use case for collections and that you understand their limitations.<br>
**</p>
<h3 id="details">Details:</h3>
<p>Here are some code examples and results that summarize what kinds of collections generate tombstones and which don't.</p>
<p>Let's create a table with a map and a frozen map.</p>
<pre><code>cqlsh&gt; CREATE TABLE test.map_test (
    a text PRIMARY KEY,
    b map&lt;text, text&gt;,
    c frozen&lt;map&lt;text, text&gt;&gt;
)
</code></pre>
<p>and add some data to each:</p>
<pre><code>cqlsh&gt; insert into map_test (a, b, c) VALUES ('a', { '1':'a' }, { '2': 'b' }) ;

cqlsh&gt; select * from test.map_test ;

 a | b          | c
---+------------+------------
 a | {'1': 'a'} | {'2': 'b'}
</code></pre>
<p>Let's see what happened under the hood using <code>sstable2json</code> after flushing:</p>
<pre><code>$ sstable2json test-map_test-ka-1-Data.db
[
{&quot;key&quot;: &quot;a&quot;,
 &quot;cells&quot;: [[&quot;&quot;,&quot;&quot;,1458266095727275],
           [&quot;b:_&quot;,&quot;b:!&quot;,1458266095727274,&quot;t&quot;,1458266095],
           [&quot;b:31&quot;,&quot;61&quot;,1458266095727275],
           [&quot;c&quot;,&quot;0000000100000001320000000162&quot;,1458266095727275]]}
]
</code></pre>
<p>Notice the t (tombstone) in b. There is no tombstone in c. This is because frozen collections are stored all together in a single cassandra cell. No tombstone necessary for inserts.</p>
<p>Now let's try an update</p>
<pre><code>$ update test.map_test SET b = { '3': 'c'}, c = {'3':'c'} where a='a' ;

cqlsh&gt; select * from test.map_test ;

 a | b          | c
---+------------+------------
 a | {'3': 'c'} | {'3': 'c'}

(1 rows)
</code></pre>
<p>After flushing we get a new sstable, also with a tombstone in b:</p>
<pre><code>$ sstable2json test-map_test-ka-2-Data.db 
[
{&quot;key&quot;: &quot;a&quot;,
 &quot;cells&quot;: [[&quot;b:_&quot;,&quot;b:!&quot;,1458266473158221,&quot;t&quot;,1458266473],
           [&quot;b:33&quot;,&quot;63&quot;,1458266473158222],
           [&quot;c&quot;,&quot;0000000100000001330000000163&quot;,1458266473158222]]}
]
</code></pre>
<p>Does a compaction get rid of the tombstone?</p>
<pre><code>$ nodetool compact

$ sstable2json test-map_test-ka-3-Data.db

[
{&quot;key&quot;: &quot;a&quot;,
 &quot;cells&quot;: [[&quot;&quot;,&quot;&quot;,1458266095727275],
           [&quot;b:_&quot;,&quot;b:!&quot;,1458266473158221,&quot;t&quot;,1458266473],
           [&quot;b:33&quot;,&quot;63&quot;,1458266473158222],
           [&quot;c&quot;,&quot;0000000100000001330000000163&quot;,1458266473158222]]}
]
</code></pre>
<p>No! remember tombstones must live longer than gc_grace AND meet the criteria in your tombstone compaction subproperties to get deleted. This helps avoid zombie data.</p>
<p>Now let's try incremental update:</p>
<pre><code>cqlsh&gt; update test.map_test SET b = b + { '4': 'd'}, c = c + {'4':'d'} where a='a' ;

InvalidRequest: code=2200 [Invalid query] message=&quot;Invalid operation (c = c + {'4':'d'}) for frozen collection column c&quot;

cqlsh&gt; update test.map_test SET b = b + { '4': 'd'} where a='a' ;

$ sstable2json test-map_test-ka-4-Data.db
[
{&quot;key&quot;: &quot;a&quot;,
 &quot;cells&quot;: [[&quot;b:34&quot;,&quot;64&quot;,1458266948817380]]}
]

</code></pre>
<p>Only the non frozen collection supports this fancy kind of updates. Notice that it did not produce a tombstone. Tombstones only happen for inserts and non incrememntal updates on non frozen collections.</p>
]]></content:encoded></item><item><title><![CDATA[Tuning DSE Search - Indexing latency and query latency]]></title><description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>DSE offers out of the box search indexing for your Cassandra data. The days of double writes or ETL's between separate DBMS and Search clusters are gone.</p>
<p>I have my cql table, I execute the following API call, and (boom) my cassandra data is available for:</p>
<ol>
<li>full text/fuzzy</li></ol>]]></description><link>https://www.sestevez.com/tuning-dse-search/</link><guid isPermaLink="false">5a56690447d72f4798d9e47e</guid><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Thu, 17 Mar 2016 22:02:39 GMT</pubDate><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>DSE offers out of the box search indexing for your Cassandra data. The days of double writes or ETL's between separate DBMS and Search clusters are gone.</p>
<p>I have my cql table, I execute the following API call, and (boom) my cassandra data is available for:</p>
<ol>
<li>full text/fuzzy search</li>
<li>ad hoc lucene secondary index powered filtering, and</li>
<li>geospatial search</li>
</ol>
<p>Here is my API call:</p>
<pre><code>$ bin/dsetool create_core &lt;keyspace&gt;.&lt;table&gt; generateResources=true reindex=true
</code></pre>
<p>or if you prefer curl (or are using basic auth) use the following:</p>
<pre><code>$ curl &quot;http://localhost:8983/solr/admin/cores?action=CREATE&amp;name=&lt;keyspace&gt;.&lt;table&gt;&amp;generateResources=true&quot;
</code></pre>
<p>Rejoice! we are in inverted index, single cluster, operational simplicity bliss!</p>
<p>The remainder of this post will be focused on <strong>advanced tuning</strong> for DSE Search both for <strong>a)</strong> search indexing latency (the time it takes for data to be searchable after it has been inserted through cql), and <strong>b)</strong> search query latency (timings for your search requests).</p>
<h2 id="indexinglatency">Indexing latency</h2>
<p>In this section I'll talk about the kinds of things we can do in order to</p>
<ol>
<li>instrument and monitor DSE Search indexing and</li>
<li>tune indexing for lower latencies and increased performance</li>
</ol>
<p><strong>Note</strong>: DSE Search ships with Real Time (RT) indexing which will give you faster indexing with 4.7.3, especially when it comes to the tails of your latency distribution. Here's one of our performance tests. It shows you real time vs near-real time indexing as of 4.7.0:</p>
<p><img src="https://s3.amazonaws.com/uploads.hipchat.com/6528/1116934/P10Ckn4e4cijTf0/upload.png" alt="indexing chart"></p>
<p>Perhaps more importantly, as you get machines with more cores, you can continue to increase your indexing performance linearly:<br>
<img src="https://s3.amazonaws.com/uploads.hipchat.com/6528/1116934/5OFvz6SgZsl68b1/Screen%20Shot%202016-03-15%20at%2011.03.22%20PM.png" alt="rt vs nrt"></p>
<p>Be aware, however, that you should only run one RT search core per cluster since it is significantly more resource hungry than near-real time (NRT).</p>
<p><strong>Side note on GC</strong>: Because solr and cassandra run on the same JVM in DSE Search and the indexing process generates a lot of java objects, running Search requires a larger JVM Heap. When running traditional <strong>CMS</strong>, we recommend a 14gb heap with about 2gb new gen. Consider the Stump's <a href="https://issues.apache.org/jira/browse/CASSANDRA-8150">CASSANDRA-8150</a> settings when running search with CMS. <strong>G1GC</strong> has been found to perform quite well with search workloads, I personally run with a 25gb heap (do not set new gen with G1, the whole point of G1 is that it sets it itself based on your workload!) and <code>gc_pause_ms</code> at about 1000 (go higher for higher throughput or lower to minimize latencies / p99's; don't go below 500). Update (thanks mc) you configure this setting in cassandra-env.sh.</p>
<h3 id="1instrumentation">1) Instrumentation</h3>
<p><strong>Index Pool Stats:</strong></p>
<p>DSE Search parallelizes the indexing process and allocates work to a thread pool for indexing of your data.</p>
<p>Using JMX, you can see statistics on your indexing threadpool depth, completion, timings, and whether backpressure is active.</p>
<p>This is important because if your indexing queues get too deep, we risk having too much heap pressure =&gt; OOM's. Backpressure will throttle commits and eventually load shed if search can't keep up with an indexing workload. Backpressure gets triggered when the queues get too large.</p>
<p>The mbean is called:</p>
<pre><code>com.datastax.bdp.search.&lt;keyspace&gt;.&lt;table&gt;.IndexPool
</code></pre>
<p><img src="https://s3.amazonaws.com/uploads.hipchat.com/6528/1116934/ObHJEFKOEuQnLm8/upload.png" alt="Indexing queues"><br>
​<br>
<strong>Commit/Update Stats:</strong></p>
<p>You can also see statistics on indexing performance (in microseconds) based on the particular stage of the indexing process for both <code>commit</code>s and <code>update</code>s.</p>
<p><strong>Commit:</strong></p>
<blockquote>
<p>The stages are:</p>
</blockquote>
<blockquote>
<p><code>FLUSH</code> - Comprising the time spent by flushing the async indexing queue.</p>
</blockquote>
<blockquote>
<p><code>EXECUTE</code> - Comprising the time spent by actually executing the commit on the index.</p>
</blockquote>
<blockquote>
<p>The mbean is called:</p>
</blockquote>
<blockquote>
<p><code>com.datastax.bdp.search.&lt;keyspace&gt;.&lt;table&gt;.CommitMetrics</code></p>
</blockquote>
<p><strong>Update:</strong></p>
<blockquote>
<p>The stages are:</p>
</blockquote>
<blockquote>
<p><code>WRITE</code> - Comprising the time spent to convert the Solr document and write it into Cassandra (only available when indexing via the Solrj HTTP APIs). If you're using cql this will be 0.<br>
<code>QUEUE</code> - Comprising the time spent by the index update task into the index pool.<br>
<code>PREPARE</code>- Comprising the time spent preparing the actual index update.<br>
<code>EXECUTE</code> - Comprising the time spent to actually executing the index update on Lucene.</p>
</blockquote>
<blockquote>
<p>The mbean is:</p>
</blockquote>
<blockquote>
<pre><code>`com.datastax.bdp.search.&lt;keyspace&gt;.&lt;table&gt;.UpdateMetrics`
</code></pre>
</blockquote>
<p><img src="https://s3.amazonaws.com/uploads.hipchat.com/6528/1116934/CLrZrQNbRatrmck/upload.png" alt="indexing stats"></p>
<p>Here, the average latency for the QUEUE stage of the <code>update</code> is 767 micros. See our docs for more details on the <a href="http://docs.datastax.com/en/latest-dse/datastax_enterprise/srch/srchCmtQryMbeans.html">metrics mbeans</a> and their stages.</p>
<h3 id="2tuning">2) Tuning</h3>
<p>Almost everything in c* and DSE is configurable. Here's the key levers to get you better search indexing performance. Based on what you see in your instrumentation you can tune accordingly.</p>
<p>The main lever is <code>soft autocommit</code>, that's the minimum amount of time that will go by before queries are available for search. With RT we can set it to 250 ms or even as low as 100ms--given the right hardware. Tune this based on your SLA's.</p>
<p>The next most important lever is concurrency per core (or <code>max_solr_concurrency_per_core</code>). You can usually set this to number of CPU cores available to maximize indexing throughput.</p>
<p>Backpressure threshold will become more important as your load increases. Larger boxes can handle higher bp thresholds.</p>
<p>Don't forget to set up the ramBuffer to 2gb per the docs when you turn on RT indexing.</p>
<h2 id="querylatency">Query Latency</h2>
<p>Now, I'll go over how we can monitor query performance in DSE Search, identify issues, and some of the tips / tricks we can use to improve search query performance. I will cover how to:</p>
<ol>
<li>instrument and monitor DSE Search indexing and</li>
<li>tune indexing for lower latencies and increased performance.</li>
</ol>
<p>Simliar to how search indexing performance scales with CPU's, search query performance scales with RAM. Keeping your search indexes in OS page cache is the biggest thing you can do to minimize latencies; so scale deliberately!</p>
<h3 id="1instrumentation">1) Instrumentation</h3>
<p>There are multiple tools available for monitoring search performance.</p>
<h4 id="opscenter">OpsCenter:</h4>
<p>OpsCenter supports a few search metrics that can be configured per node, datacenter, and solr core:</p>
<ol>
<li>search latencies</li>
<li>search requests</li>
<li>index size</li>
<li>search timeouts</li>
<li>search errors</li>
</ol>
<p><img src="https://s3.amazonaws.com/uploads.hipchat.com/6528/1116934/uoq1hLRQ58AZBhn/Screen%20Shot%202016-03-15%20at%2011.47.12%20PM.png" alt="opscenter"></p>
<h4 id="metricsmbeans">Metrics mbeans:</h4>
<p>In the same way that indexing has performance metrics, DSE Search <a href="https://docs.datastax.com/en/datastax_enterprise/4.0/datastax_enterprise/srch/srchQryMbean.html">query performance metrics</a> are available through JMX and can be useful for troubleshooting perofrmance issues. We can use the <code>query.name</code> parameter in your DSE Serch queries to capture metrics for specifically tagged queries.</p>
<p><strong>Query:</strong></p>
<p>The stages are:</p>
<blockquote>
<p><code>COORDINATE</code> - Comprises the total amount of time spent by the coordinator node to distribute the query and gather/process results from shards. This value is computed only on query coordinator nodes.</p>
</blockquote>
<blockquote>
<p><code>EXECUTE</code> - Comprises the time spent by a single shard to execute the actual index query. This value is computed on the local node executing the shard query.</p>
</blockquote>
<blockquote>
<p><code>RETRIEVE</code> - Comprises the time spent by a single shard to retrieve the actual data from Cassandra. This value will be computed on the local node hosting the requested data.</p>
</blockquote>
<p>The mbean is:</p>
<blockquote>
<p><code>com.datastax.bdp.search.&lt;keyspace&gt;.&lt;table&gt;.QueryMetrics</code></p>
</blockquote>
<h4 id="querytracing">Query Tracing:</h4>
<p>When using <code>solr_query</code> via cql, query tracing can provide  useful information as to where a particular query spent time in the cluster.</p>
<p>Query tracing is available in cqlsh <code>tracing on</code>, in devcenter (in the tab at the bottom of the screen), and via probabilistic tracing which is configurable via <a href="https://docs.datastax.com/en/cassandra/2.1/cassandra/tools/toolsSetTraceProbability.html">nodetool</a>.</p>
<h4 id="dsesearchslowquerylog">DSE Search slow query log:</h4>
<p>When users complain about a slow query and you need to find out what it is, the DSE Search slow query log is a good starting point.</p>
<pre><code>dsetool perf solrslowlog enable
</code></pre>
<p>Stores to a table in cassandra in the <code>dse_perf.solr_slow_sub_query_log</code> table</p>
<h3 id="2tuning">2) Tuning</h3>
<p>Now let's focus on some tips for how you can improve search query performance.</p>
<h3 id="indexsize">Index size</h3>
<p>Index size is so important that, I wrote <a href="http://www.sestevez.com/solr-space-saving-profile/">a separate post</a> just on that subject:</p>
<h4 id="qvsfq">Q vs. FQ</h4>
<p>In order to take advantage of the solr filter cache, build your queries using fq not q. The filter cache is the only solr cache that persists across commits so don't spend time or valuable RAM trying to leverage the other caches.</p>
<h4 id="solrqueryrouting">Solr query routing</h4>
<p>Partition routing is a great multi-tennancy feature in DSE Search that lets you limit the amount of fan out that a search query will take under the hood. Essentially, you're able to specify a Cassandra partition that you are interested in limiting your search to. This will limit the number of nodes that DSE Search requires to fullfil your request.</p>
<h4 id="usedocvaluesforfacetingandsorting">Use docvalues for Faceting and Sorting.</h4>
<p>To get improved performance and to avoid OOMs from the field cache, always remember to turn on docvalues on fields that you will be sorting and faceting over. This may become mandatory in DSE at some point so plan ahead.</p>
<h3 id="otherdsedifferentiators">Other DSE Differentiators</h3>
<p>If you're comparing DSE Search against other search offerings / technologies, the following two differentiators are unique to DSE Search.</p>
<h4 id="faulttolerantdistributedqueries">Fault tolerant distributed queries</h4>
<p>If a node dies during a query, we retry the query on another node.</p>
<h4 id="nodehealth">Node health</h4>
<p>Node health and shard router behavior.<br>
DSE Search monitors node health and makes distributed query routing decisions based on the following:</p>
<ol>
<li>Uptime: a node that just started may well be lacking the most up-to-date data (to be repaired via HH or AE).</li>
<li>Number of dropped mutations.</li>
<li>Number of hints the node is a target for.</li>
<li>&quot;failed reindex&quot; status.</li>
</ol>
<p>All you need to take advantage of this is be on a modern DSE version.</p>
]]></content:encoded></item><item><title><![CDATA[Things you didn't think you could do with DSE Search and CQL]]></title><description><![CDATA[<h4 id="intro">Intro</h4>
<p>CQL and DSE Search promise to make access to a lucene backed index scalable, highly avaliable, operationally simple, and user friendly.</p>
<p>There have been a couple of developments in DSE 4.8 point releases that may have gone unnoticed by the community of DSE Search users.</p>
<p>One of the</p>]]></description><link>https://www.sestevez.com/advanced-solr-cql/</link><guid isPermaLink="false">5a56690547d72f4798d9e48c</guid><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Fri, 11 Mar 2016 19:55:48 GMT</pubDate><content:encoded><![CDATA[<h4 id="intro">Intro</h4>
<p>CQL and DSE Search promise to make access to a lucene backed index scalable, highly avaliable, operationally simple, and user friendly.</p>
<p>There have been a couple of developments in DSE 4.8 point releases that may have gone unnoticed by the community of DSE Search users.</p>
<p>One of the main benefits of using DSE Search is that you are able to query the search indexes from through CQL directly from your favorite DataStax driver. Avoiding the solr HTTP API all together means that you:</p>
<ol>
<li>
<p>Don't need to two sets of DAO's in your app and have application logic around which to use for what purpose</p>
</li>
<li>
<p>You don't need a load balancer in front of Solr/Tomcat because the DataStax drivers are cluster aware and will load balance for you</p>
</li>
<li>
<p>You don't need to worry about one node going down under your load balancer and having a fraction of your queries failing upon node failure</p>
</li>
<li>
<p>When security is enabled, requests through the HTTP API are significantly slower, to quote the DSE docs:</p>
</li>
</ol>
<blockquote>
<p>&quot;Due to the stateless nature of HTTP Basic Authentication, this can have a significant performance impact as the authentication process must be executed on each HTTP request.&quot;</p>
</blockquote>
<h4 id="whyeverusethehttpapi">Why ever use the HTTP API?</h4>
<p>The CQL interface is designed to return rows and columns, so features like Solr's numFound and faceting, were not built in the first few releases.</p>
<p>These features have snuck in via patches in point releases and users that aren't studiously reading the <a href="http://docs.datastax.com/en/latest-dse/datastax_enterprise/RNdse.html?scroll=relnotes48__481Chgs">release notes</a> may not have noticed the changes.</p>
<p>How would I go about getting numfound and performing facet queries in the latest (DSE 4.8.1+) version of DSE?</p>
<h4 id="showmehow">Show me how</h4>
<p>If you know you just need the count (and not the data that comes along with it) then you can just specify count(*) and keep the solr_query where clause. DSE intercepts the query and brings back numDocs from DSE Search instead of actually performing the count in cassandra:</p>
<pre><code>SELECT count(*) FROM test.pymt WHERE solr_query = '{&quot;q&quot;:&quot;countryoftravel:\&quot;United States\&quot;&quot;}' ;

 count
-------
 39709 
</code></pre>
<p>Here it is with tracing enabled, notice that even my wide open count(*) query comes back in micros</p>
<pre><code>cqlsh&gt; SELECT count(*) FROM test.pymt WHERE solr_query = '{&quot;q&quot;:&quot;*:*&quot;}' ;

 count
--------
 817000

(1 rows)

Tracing session: 7020df80-e7a9-11e5-9c31-37116dd067c6

 activity                                                                                        | timestamp                  | source    | source_elapsed
-------------------------------------------------------------------------------------------------+----------------------------+-----------+----------------
                                                                              Execute CQL3 query | 2016-03-11 11:51:02.136000 | 127.0.0.1 |              0
 Parsing SELECT count(*) FROM test.pymt WHERE solr_query = '{&quot;q&quot;:&quot;*:*&quot;}' ; [SharedPool-Worker-1] | 2016-03-11 11:51:02.136000 | 127.0.0.1 |             34
                                                       Preparing statement [SharedPool-Worker-1] | 2016-03-11 11:51:02.136000 | 127.0.0.1 |             84
                                                                                Request complete | 2016-03-11 11:51:02.146918 | 127.0.0.1 |          10918
</code></pre>
<p>The same goes for facet queries. Note that because of the way the cql protocol is designed (around rows and columns), DSE returns the facet results inside a single cell in JSON format. Pretty slick:</p>
<pre><code>select * FROM test.pymt WHERE solr_query='{&quot;facet&quot;:{&quot;pivot&quot;:&quot;physicianprimarytype&quot;},&quot;q&quot;:&quot;*:*&quot;}' ;
 facet_pivot
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 {&quot;physicianprimarytype&quot;:[{&quot;field&quot;:&quot;physicianprimarytype&quot;,&quot;value&quot;:&quot;doctor&quot;,&quot;count&quot;:813638},{&quot;field&quot;:&quot;physicianprimarytype&quot;,&quot;value&quot;:&quot;medical&quot;,&quot;count&quot;:720967},{&quot;field&quot;:&quot;physicianprimarytype&quot;,&quot;value&quot;:&quot;of&quot;,&quot;count&quot;:92671},{&quot;field&quot;:&quot;physicianprimarytype&quot;,&quot;value&quot;:&quot;osteopathy&quot;,&quot;count&quot;:60123},{&quot;field&quot;:&quot;physicianprimarytype&quot;,&quot;value&quot;:&quot;dentistry&quot;,&quot;count&quot;:17132},{&quot;field&quot;:&quot;physicianprimarytype&quot;,&quot;value&quot;:&quot;optometry&quot;,&quot;count&quot;:11447},{&quot;field&quot;:&quot;physicianprimarytype&quot;,&quot;value&quot;:&quot;medicine&quot;,&quot;count&quot;:3969},{&quot;field&quot;:&quot;physicianprimarytype&quot;,&quot;value&quot;:&quot;podiatric&quot;,&quot;count&quot;:3969},{&quot;field&quot;:&quot;&quot;physicianprimarytype&quot;,&quot;value&quot;:&quot;chiropractor&quot;,&quot;count&quot;:192}]}
</code></pre>
<h4 id="tldr">TL;DR</h4>
<p>You don't have to use the HTTP API for seach queries, even if you need numFound and faceting. It is now supported via CQL and solr_query.</p>
<h4 id="futures">Futures</h4>
<p>Remember I mentioned that the cql protocol is desinged round rows and columns? Well check out this ticket resolved in C* 2.2.0 beta 1 <a href="https://issues.apache.org/jira/browse/CASSANDRA-8553">CASSANDRA-8553</a>. If you use your imagination, there are some improvements that can be made once DSE gets c* 3.0 under the hood to make Search functionality even more slick.</p>
<p>Stay tuned!</p>
<h4 id="morefeatures">More Features!</h4>
<p>I meant to stop here but when I asked folks (char0) to review this post, they had some additional DSE Search features that get overlooked. I'll breifly describe them and link to documentation. If you're new to DSE Search definitely read on:</p>
<h5 id="partitonrouting">Partiton routing:</h5>
<p><a href="http://docs.datastax.com/en/datastax_enterprise/4.8/datastax_enterprise/srch/srchRouteQuery.html#srchRouteQuery__srchRouteQuery_unique_1">Partition routing</a> is a great multi-tennant feature that lets you limit the amount of fan out that a search query will take under the hood.<br>
Essentially, you're able to specify a Cassandra partition that you are interested in limiting your search to. This will limit the number of nodes that DSE Search requires to fullfil your request.</p>
<h5 id="jsonqueries">JSON queries</h5>
<p>If you're looking to do advanced queries thorugh cql (beyond just a simple search) check out the <a href="http://docs.datastax.com/en/latest-dse/datastax_enterprise/srch/srchJSON.html?scroll=srchJSON__srchJSONsinglePh">datastax documentation for json queries</a>.</p>
<h5 id="timeallowed">timeAllowed</h5>
<p>Many search use cases don't actually require the backend to scan the entire dataset. If you're just trying to fill out a page with search results, and latency matters more than having a complete results set (when you dont care about numFound), the <a href="https://wiki.apache.org/solr/CommonQueryParameters">timeAllowed</a> parameter let's you set a maximum latency and DSE Search will just return the results it has found so far.</p>
<p>Please comment if you have any additional Search DSE Features that you think are overlooked!</p>
]]></content:encoded></item><item><title><![CDATA[Minimizing DSE Search (solr) Indexes]]></title><description><![CDATA[<h4 id="introwhy">Intro / why?</h4>
<p>Search query performance depends on our ability to utilize the OS page cache effectively to keep search indexes hot. The smaller the size of your indexes, the easier it will be for the OS to maintain them in memory.</p>
<p>This article shows 6 tactics that can be used</p>]]></description><link>https://www.sestevez.com/solr-space-saving-profile/</link><guid isPermaLink="false">5a56690547d72f4798d9e48a</guid><dc:creator><![CDATA[Sebastian Estevez]]></dc:creator><pubDate>Tue, 19 Jan 2016 15:52:00 GMT</pubDate><content:encoded><![CDATA[<h4 id="introwhy">Intro / why?</h4>
<p>Search query performance depends on our ability to utilize the OS page cache effectively to keep search indexes hot. The smaller the size of your indexes, the easier it will be for the OS to maintain them in memory.</p>
<p>This article shows 6 tactics that can be used to minimize the size of your DSE Search index.</p>
<h4 id="tactics">Tactics</h4>
<p>Here are the tactics you can employ to minimize your DSE Search index size:</p>
<ol>
<li>Turn off <a href="http://wiki.apache.org/solr/TermVectorComponent">Term Vector</a> information if you're not using highlighting or other functionality that relies on it:</li>
</ol>
<p>• <code>termVectors=&quot;false&quot;</code></p>
<p>• <code>termPositions=&quot;false&quot;</code></p>
<p>• <code>termOffsets=&quot;false&quot;</code></p>
<ol start="2">
<li>Turn on <a href="https://wiki.apache.org/solr/SchemaXml">omit norms</a> if you're not using Boosts:</li>
</ol>
<p>• <code>omitNorms=&quot;true&quot;</code></p>
<p><em><strong>Note:</strong></em> From what I've seen term vectors and omit norms can be a substantial percentage of your index ~50%</p>
<ol start="3">
<li>
<p>Only index fields you intend to search. Most use cases don't require users to index all their fields for search.</p>
</li>
<li>
<p>Make sure you're not indexing your <code>_partition_key</code> (this may happen by default in modern DSE versions):</p>
</li>
</ol>
<p><code>&lt;field name=&quot;_partitionKey&quot; type=&quot;uuid&quot; indexed=&quot;false&quot;/&gt;</code></p>
<ol start="5">
<li>
<p>Use StrField rather than TextField (no tokenizers)</p>
</li>
<li>
<p>TrieField <a href="https://lucene.apache.org/core/3_6_2/api/core/org/apache/lucene/search/NumericRangeQuery.html#precisionStepDesc">precisionStep</a> - A higher precision step will increase query latency but it will decrease the index size.</p>
</li>
</ol>
<h4 id="learnmoreaboutyourindexes">Learn more about your indexes</h4>
<p>You can also introspect your indexes using Luke. Luke is bundled in DSE so you can access it from a browser by hitting:</p>
<p>http://:8983/solr/./admin/luke?&amp;numTerms=0</p>
]]></content:encoded></item></channel></rss>