blob: c64e7d65b7e12f7d254e3a26d20ce9c062573298 [file] [log] [blame]
<!doctype html>
<html lang="en" dir="ltr" class="docs-wrapper docs-doc-page docs-version-current plugin-docs plugin-id-default docs-doc-id-development/modules" data-has-hydrated="false">
<head>
<meta charset="UTF-8">
<meta name="generator" content="Docusaurus v2.4.3">
<title data-rh="true">Creating extensions | Apache® Druid</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://druid.apache.org/img/druid_nav.png"><meta data-rh="true" name="twitter:image" content="https://druid.apache.org/img/druid_nav.png"><meta data-rh="true" property="og:url" content="https://druid.apache.org/docs/29.0.0/development/modules"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Creating extensions | Apache® Druid"><meta data-rh="true" name="description" content="&lt;!--"><meta data-rh="true" property="og:description" content="&lt;!--"><link data-rh="true" rel="icon" href="/img/favicon.png"><link data-rh="true" rel="canonical" href="https://druid.apache.org/docs/29.0.0/development/modules"><link data-rh="true" rel="alternate" href="https://druid.apache.org/docs/29.0.0/development/modules" hreflang="en"><link data-rh="true" rel="alternate" href="https://druid.apache.org/docs/29.0.0/development/modules" hreflang="x-default"><link rel="preconnect" href="https://www.google-analytics.com">
<link rel="preconnect" href="https://www.googletagmanager.com">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-131010415-1"></script>
<script>function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","UA-131010415-1",{})</script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.4/clipboard.min.js"></script><link rel="stylesheet" href="/assets/css/styles.4b88a1d0.css">
<link rel="preload" href="/assets/js/runtime~main.63d85568.js" as="script">
<link rel="preload" href="/assets/js/main.7181d856.js" as="script">
</head>
<body class="navigation-with-keyboard">
<script>!function(){function t(t){document.documentElement.setAttribute("data-theme",t)}var e=function(){var t=null;try{t=new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}return t}()||function(){var t=null;try{t=localStorage.getItem("theme")}catch(t){}return t}();t(null!==e?e:"light")}()</script><div id="__docusaurus">
<div role="region" aria-label="Skip to main content"><a class="skipToContent_fXgn" href="#__docusaurus_skipToContent_fallback">Skip to main content</a></div><nav aria-label="Main" class="navbar navbar--fixed-top navbar--dark"><div class="navbar__inner"><div class="navbar__items"><button aria-label="Toggle navigation bar" aria-expanded="false" class="navbar__toggle clean-btn" type="button"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/"><div class="navbar__logo"><img src="/img/druid_nav.png" alt="Apache® Druid" class="themedImage_ToTc themedImage--light_HNdA"><img src="/img/druid_nav.png" alt="Apache® Druid" class="themedImage_ToTc themedImage--dark_i4oU"></div></a></div><div class="navbar__items navbar__items--right"><a class="navbar__item navbar__link" href="/technology">Technology</a><a class="navbar__item navbar__link" href="/use-cases">Use Cases</a><a class="navbar__item navbar__link" href="/druid-powered">Powered By</a><a class="navbar__item navbar__link" href="/docs/29.0.0/design/">Docs</a><a class="navbar__item navbar__link" href="/community/">Community</a><div class="navbar__item dropdown dropdown--hoverable dropdown--right"><a href="#" aria-haspopup="true" aria-expanded="false" role="button" class="navbar__link">Apache®</a><ul class="dropdown__menu"><li><a href="https://www.apache.org/" target="_blank" rel="noopener noreferrer" class="dropdown__link">Foundation<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a></li><li><a href="https://apachecon.com/?ref=druid.apache.org" target="_blank" rel="noopener noreferrer" class="dropdown__link">Events<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a></li><li><a href="https://www.apache.org/licenses/" target="_blank" rel="noopener noreferrer" class="dropdown__link">License<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a></li><li><a href="https://www.apache.org/foundation/thanks.html" target="_blank" rel="noopener noreferrer" class="dropdown__link">Thanks<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a></li><li><a href="https://www.apache.org/security/" target="_blank" rel="noopener noreferrer" class="dropdown__link">Security<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a></li><li><a href="https://www.apache.org/foundation/sponsorship.html" target="_blank" rel="noopener noreferrer" class="dropdown__link">Sponsorship<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a></li></ul></div><a class="navbar__item navbar__link" href="/downloads/">Download</a><div class="searchBox_ZlJk"><div class="navbar__search"><span aria-label="expand searchbar" role="button" class="search-icon" tabindex="0"></span><input type="search" id="search_input_react" placeholder="Loading..." aria-label="Search" class="navbar__search-input search-bar" disabled=""></div></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div id="__docusaurus_skipToContent_fallback" class="main-wrapper mainWrapper_z2l0 docsWrapper_BCFX"><button aria-label="Scroll back to top" class="clean-btn theme-back-to-top-button backToTopButton_sjWU" type="button"></button><div class="docPage__5DB"><aside class="theme-doc-sidebar-container docSidebarContainer_b6E3"><div class="sidebarViewport_Xe31"><div class="sidebar_njMd"><nav aria-label="Docs sidebar" class="menu thin-scrollbar menu_SIkG"><ul class="theme-doc-sidebar-menu menu__list"><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/docs/29.0.0/design/">Getting started</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/docs/29.0.0/tutorials/tutorial-msq-extern">Tutorials</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/docs/29.0.0/design/architecture">Design</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/docs/29.0.0/ingestion/">Ingestion</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/docs/29.0.0/data-management/">Data management</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/docs/29.0.0/querying/sql">Querying</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/docs/29.0.0/api-reference/">API reference</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/docs/29.0.0/configuration/">Configuration</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/docs/29.0.0/operations/web-console">Operations</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret menu__link--active" aria-expanded="true" href="/docs/29.0.0/development/overview">Development</a></div><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/docs/29.0.0/development/overview">Developing on Druid</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link menu__link--active" aria-current="page" tabindex="0" href="/docs/29.0.0/development/modules">Creating extensions</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/docs/29.0.0/development/javascript">JavaScript functionality</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/docs/29.0.0/development/build">Build from source</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/docs/29.0.0/development/versioning">Versioning</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/docs/29.0.0/development/contribute-to-docs">Contribute to Druid docs</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/docs/29.0.0/development/experimental">Experimental features</a></li></ul></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/docs/29.0.0/misc/papers-and-talks">Misc</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/docs/29.0.0/release-info/release-notes">Release info</a></div></li></ul></nav></div></div></aside><main class="docMainContainer_gTbr"><div class="container padding-top--md padding-bottom--lg"><div class="row"><div class="col docItemCol_VOVn"><div class="docItemContainer_Djhp"><article><nav class="theme-doc-breadcrumbs breadcrumbsContainer_Z_bl" aria-label="Breadcrumbs"><ul class="breadcrumbs" itemscope="" itemtype="https://schema.org/BreadcrumbList"><li class="breadcrumbs__item"><a aria-label="Home page" class="breadcrumbs__link" href="/"><svg viewBox="0 0 24 24" class="breadcrumbHomeIcon_YNFT"><path d="M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z" fill="currentColor"></path></svg></a></li><li class="breadcrumbs__item"><span class="breadcrumbs__link">Development</span><meta itemprop="position" content="1"></li><li itemscope="" itemprop="itemListElement" itemtype="https://schema.org/ListItem" class="breadcrumbs__item breadcrumbs__item--active"><span class="breadcrumbs__link" itemprop="name">Creating extensions</span><meta itemprop="position" content="2"></li></ul></nav><div class="tocCollapsible_ETCw theme-doc-toc-mobile tocMobile_ITEo"><button type="button" class="clean-btn tocCollapsibleButton_TO0P">On this page</button></div><div class="theme-doc-markdown markdown"><header><h1>Creating extensions</h1></header><p>Druid uses a module system that allows for the addition of extensions at runtime.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="writing-your-own-extensions">Writing your own extensions<a href="#writing-your-own-extensions" class="hash-link" aria-label="Direct link to Writing your own extensions" title="Direct link to Writing your own extensions"></a></h2><p>Druid&#x27;s extensions leverage Guice in order to add things at runtime. Basically, Guice is a framework for Dependency Injection, but we use it to hold the expected object graph of the Druid process. Extensions can make any changes they want/need to the object graph via adding Guice bindings. While the extensions actually give you the capability to change almost anything however you want, in general, we expect people to want to extend one of the things listed below. This means that we honor our <a href="/docs/29.0.0/development/versioning">versioning strategy</a> for changes that affect the interfaces called out on this page, but other interfaces are deemed &quot;internal&quot; and can be changed in an incompatible manner even between patch releases.</p><ol><li>Add a new deep storage implementation by extending the <code>org.apache.druid.segment.loading.DataSegment*</code> and
<code>org.apache.druid.tasklogs.TaskLog*</code> classes.</li><li>Add a new input source by extending <code>org.apache.druid.data.input.InputSource</code>.</li><li>Add a new input entity by extending <code>org.apache.druid.data.input.InputEntity</code>.</li><li>Add a new input source reader if necessary by extending <code>org.apache.druid.data.input.InputSourceReader</code>. You can use <code>org.apache.druid.data.input.impl.InputEntityIteratingReader</code> in most cases.</li><li>Add a new input format by extending <code>org.apache.druid.data.input.InputFormat</code>.</li><li>Add a new input entity reader by extending <code>org.apache.druid.data.input.TextReader</code> for text formats or <code>org.apache.druid.data.input.IntermediateRowParsingReader</code> for binary formats.</li><li>Add Aggregators by extending <code>org.apache.druid.query.aggregation.AggregatorFactory</code>, <code>org.apache.druid.query.aggregation.Aggregator</code>,
and <code>org.apache.druid.query.aggregation.BufferAggregator</code>.</li><li>Add PostAggregators by extending <code>org.apache.druid.query.aggregation.PostAggregator</code>.</li><li>Add ExtractionFns by extending <code>org.apache.druid.query.extraction.ExtractionFn</code>.</li><li>Add Complex metrics by extending <code>org.apache.druid.segment.serde.ComplexMetricSerde</code>.</li><li>Add new Query types by extending <code>org.apache.druid.query.QueryRunnerFactory</code>, <code>org.apache.druid.query.QueryToolChest</code>, and
<code>org.apache.druid.query.Query</code>.</li><li>Add new Jersey resources by calling <code>Jerseys.addResource(binder, clazz)</code>.</li><li>Add new Jetty filters by extending <code>org.apache.druid.server.initialization.jetty.ServletFilterHolder</code>.</li><li>Add new secret providers by extending <code>org.apache.druid.metadata.PasswordProvider</code>.</li><li>Add new dynamic configuration providers by extending <code>org.apache.druid.metadata.DynamicConfigProvider</code>.</li><li>Add new ingest transform by implementing the <code>org.apache.druid.segment.transform.Transform</code> interface from the <code>druid-processing</code> package.</li><li>Bundle your extension with all the other Druid extensions</li></ol><p>Extensions are added to the system via an implementation of <code>org.apache.druid.initialization.DruidModule</code>.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="creating-a-druid-module">Creating a Druid Module<a href="#creating-a-druid-module" class="hash-link" aria-label="Direct link to Creating a Druid Module" title="Direct link to Creating a Druid Module"></a></h3><p>The DruidModule class is has two methods</p><ol><li>A <code>configure(Binder)</code> method</li><li>A <code>getJacksonModules()</code> method</li></ol><p>The <code>configure(Binder)</code> method is the same method that a normal Guice module would have.</p><p>The <code>getJacksonModules()</code> method provides a list of Jackson modules that are used to help initialize the Jackson ObjectMapper instances used by Druid. This is how you add extensions that are instantiated via Jackson (like AggregatorFactory and InputSource objects) to Druid.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="registering-your-druid-module">Registering your Druid Module<a href="#registering-your-druid-module" class="hash-link" aria-label="Direct link to Registering your Druid Module" title="Direct link to Registering your Druid Module"></a></h3><p>Once you have your DruidModule created, you will need to package an extra file in the <code>META-INF/services</code> directory of your jar. This is easiest to accomplish with a maven project by creating files in the <code>src/main/resources</code> directory. There are examples of this in the Druid code under the <code>cassandra-storage</code>, <code>hdfs-storage</code> and <code>s3-extensions</code> modules, for examples.</p><p>The file that should exist in your jar is</p><p><code>META-INF/services/org.apache.druid.initialization.DruidModule</code></p><p>It should be a text file with a new-line delimited list of package-qualified classes that implement DruidModule like</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">org.apache.druid.storage.cassandra.CassandraDruidModule</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>If your jar has this file, then when it is added to the classpath or as an extension, Druid will notice the file and will instantiate instances of the Module. Your Module should have a default constructor, but if you need access to runtime configuration properties, it can have a method with @Inject on it to get a Properties object injected into it from Guice.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="adding-a-new-deep-storage-implementation">Adding a new deep storage implementation<a href="#adding-a-new-deep-storage-implementation" class="hash-link" aria-label="Direct link to Adding a new deep storage implementation" title="Direct link to Adding a new deep storage implementation"></a></h3><p>Check the <code>azure-storage</code>, <code>google-storage</code>, <code>cassandra-storage</code>, <code>hdfs-storage</code> and <code>s3-extensions</code> modules for examples of how to do this.</p><p>The basic idea behind the extension is that you need to add bindings for your DataSegmentPusher and DataSegmentPuller objects. The way to add them is something like (taken from HdfsStorageDruidModule)</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">Binders.dataSegmentPullerBinder(binder)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .addBinding(&quot;hdfs&quot;)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .to(HdfsDataSegmentPuller.class).in(LazySingleton.class);</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">Binders.dataSegmentPusherBinder(binder)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .addBinding(&quot;hdfs&quot;)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .to(HdfsDataSegmentPusher.class).in(LazySingleton.class);</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><code>Binders.dataSegment*Binder()</code> is a call provided by the druid-core jar which sets up a Guice multibind &quot;MapBinder&quot;. If that doesn&#x27;t make sense, don&#x27;t worry about it, just think of it as a magical incantation.</p><p><code>addBinding(&quot;hdfs&quot;)</code> for the Puller binder creates a new handler for loadSpec objects of type &quot;hdfs&quot;. For the Pusher binder it creates a new type value that you can specify for the <code>druid.storage.type</code> parameter.</p><p><code>to(...).in(...);</code> is normal Guice stuff.</p><p>In addition to DataSegmentPusher and DataSegmentPuller, you can also bind:</p><ul><li>DataSegmentKiller: Removes segments, used as part of the Kill Task to delete unused segments, i.e. perform garbage collection of segments that are either superseded by newer versions or that have been dropped from the cluster.</li><li>DataSegmentMover: Allow migrating segments from one place to another, currently this is only used as part of the MoveTask to move unused segments to a different S3 bucket or prefix, typically to reduce storage costs of unused data (e.g. move to glacier or cheaper storage)</li><li>DataSegmentArchiver: Just a wrapper around Mover, but comes with a preconfigured target bucket/path, so it doesn&#x27;t have to be specified at runtime as part of the ArchiveTask.</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="validating-your-deep-storage-implementation">Validating your deep storage implementation<a href="#validating-your-deep-storage-implementation" class="hash-link" aria-label="Direct link to Validating your deep storage implementation" title="Direct link to Validating your deep storage implementation"></a></h3><p><strong>WARNING!</strong> This is not a formal procedure, but a collection of hints to validate if your new deep storage implementation is able do push, pull and kill segments.</p><p>It&#x27;s recommended to use batch ingestion tasks to validate your implementation.
The segment will be automatically rolled up to Historical note after ~20 seconds.
In this way, you can validate both push (at realtime process) and pull (at Historical process) segments.</p><ul><li>DataSegmentPusher</li></ul><p>Wherever your data storage (cloud storage service, distributed file system, etc.) is, you should be able to see one new file: <code>index.zip</code> (<code>partitionNum_index.zip</code> for HDFS data storage) after your ingestion task ends.</p><ul><li>DataSegmentPuller</li></ul><p>After ~20 secs your ingestion task ends, you should be able to see your Historical process trying to load the new segment.</p><p>The following example was retrieved from a Historical process configured to use Azure for deep storage:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">2015-04-14T02:42:33,450 INFO [ZkCoordinator-0] org.apache.druid.server.coordination.ZkCoordinator - New request[LOAD: dde_2015-01-02T00:00:00.000Z_2015-01-03T00:00:00</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">.000Z_2015-04-14T02:41:09.484Z] with zNode[/druid/dev/loadQueue/192.168.33.104:8081/dde_2015-01-02T00:00:00.000Z_2015-01-03T00:00:00.000Z_2015-04-14T02:41:09.</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">484Z].</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">2015-04-14T02:42:33,451 INFO [ZkCoordinator-0] org.apache.druid.server.coordination.ZkCoordinator - Loading segment dde_2015-01-02T00:00:00.000Z_2015-01-03T00:00:00.0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">00Z_2015-04-14T02:41:09.484Z</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">2015-04-14T02:42:33,463 INFO [ZkCoordinator-0] org.apache.druid.guice.JsonConfigurator - Loaded class[class org.apache.druid.storage.azure.AzureAccountConfig] from props[drui</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">d.azure.] as [org.apache.druid.storage.azure.AzureAccountConfig@759c9ad9]</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">2015-04-14T02:49:08,275 INFO [ZkCoordinator-0] org.apache.druid.utils.CompressionUtils - Unzipping file[/opt/druid/tmp/compressionUtilZipCache1263964429587449785.z</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">ip] to [/opt/druid/zk_druid/dde/2015-01-02T00:00:00.000Z_2015-01-03T00:00:00.000Z/2015-04-14T02:41:09.484Z/0]</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">2015-04-14T02:49:08,276 INFO [ZkCoordinator-0] org.apache.druid.storage.azure.AzureDataSegmentPuller - Loaded 1196 bytes from [dde/2015-01-02T00:00:00.000Z_2015-01-03</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">T00:00:00.000Z/2015-04-14T02:41:09.484Z/0/index.zip] to [/opt/druid/zk_druid/dde/2015-01-02T00:00:00.000Z_2015-01-03T00:00:00.000Z/2015-04-14T02:41:09.484Z/0]</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">2015-04-14T02:49:08,277 WARN [ZkCoordinator-0] org.apache.druid.segment.loading.SegmentLocalCacheManager - Segment [dde_2015-01-02T00:00:00.000Z_2015-01-03T00:00:00.000Z_2015-04-14T02:41:09.484Z] is different than expected size. Expected [0] found [1196]</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">2015-04-14T02:49:08,282 INFO [ZkCoordinator-0] org.apache.druid.server.coordination.BatchDataSegmentAnnouncer - Announcing segment[dde_2015-01-02T00:00:00.000Z_2015-01-03T00:00:00.000Z_2015-04-14T02:41:09.484Z] at path[/druid/dev/segments/192.168.33.104:8081/192.168.33.104:8081_historical__default_tier_2015-04-14T02:49:08.282Z_7bb87230ebf940188511dd4a53ffd7351]</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">2015-04-14T02:49:08,292 INFO [ZkCoordinator-0] org.apache.druid.server.coordination.ZkCoordinator - Completed request [LOAD: dde_2015-01-02T00:00:00.000Z_2015-01-03T00:00:00.000Z_2015-04-14T02:41:09.484Z]</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li>DataSegmentKiller</li></ul><p>The easiest way of testing the segment killing is marking a segment as not used and then starting a killing task in the <a href="/docs/29.0.0/operations/web-console">web console</a>.</p><p>To mark a segment as not used, you need to connect to your metadata storage and update the <code>used</code> column to <code>false</code> on the segment table rows.</p><p>To start a segment killing task, you need to access the web console then select <code>issue kill task</code> for the appropriate datasource.</p><p>After the killing task ends, <code>index.zip</code> (<code>partitionNum_index.zip</code> for HDFS data storage) file should be deleted from the data storage.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="adding-support-for-a-new-input-source">Adding support for a new input source<a href="#adding-support-for-a-new-input-source" class="hash-link" aria-label="Direct link to Adding support for a new input source" title="Direct link to Adding support for a new input source"></a></h3><p>Adding support for a new input source requires to implement three interfaces, i.e., <code>InputSource</code>, <code>InputEntity</code>, and <code>InputSourceReader</code>.
<code>InputSource</code> is to define where the input data is stored. <code>InputEntity</code> is to define how data can be read in parallel
in <a href="/docs/29.0.0/ingestion/native-batch">native parallel indexing</a>.
<code>InputSourceReader</code> defines how to read your new input source and you can simply use the provided <code>InputEntityIteratingReader</code> in most cases.</p><p>There is an example of this in the <code>druid-s3-extensions</code> module with the <code>S3InputSource</code> and <code>S3Entity</code>.</p><p>Adding an InputSource is done almost entirely through the Jackson Modules instead of Guice. Specifically, note the implementation</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">@Override</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">public List&lt;? extends Module&gt; getJacksonModules()</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">{</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> return ImmutableList.of(</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> new SimpleModule().registerSubtypes(new NamedType(S3InputSource.class, &quot;s3&quot;))</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> );</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>This is registering the InputSource with Jackson&#x27;s polymorphic serialization/deserialization layer. More concretely, having this will mean that if you specify a <code>&quot;inputSource&quot;: { &quot;type&quot;: &quot;s3&quot;, ... }</code> in your IO config, then the system will load this InputSource for your <code>InputSource</code> implementation.</p><p>Note that inside of Druid, we have made the <code>@JacksonInject</code> annotation for Jackson deserialized objects actually use the base Guice injector to resolve the object to be injected. So, if your InputSource needs access to some object, you can add a <code>@JacksonInject</code> annotation on a setter and it will get set on instantiation.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="adding-support-for-a-new-data-format">Adding support for a new data format<a href="#adding-support-for-a-new-data-format" class="hash-link" aria-label="Direct link to Adding support for a new data format" title="Direct link to Adding support for a new data format"></a></h3><p>Adding support for a new data format requires implementing two interfaces, i.e., <code>InputFormat</code> and <code>InputEntityReader</code>.
<code>InputFormat</code> is to define how your data is formatted. <code>InputEntityReader</code> is to define how to parse your data and convert into Druid <code>InputRow</code>.</p><p>There is an example in the <code>druid-orc-extensions</code> module with the <code>OrcInputFormat</code> and <code>OrcReader</code>.</p><p>Adding an InputFormat is very similar to adding an InputSource. They operate purely through Jackson and thus should just be additions to the Jackson modules returned by your DruidModule.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="adding-aggregators">Adding Aggregators<a href="#adding-aggregators" class="hash-link" aria-label="Direct link to Adding Aggregators" title="Direct link to Adding Aggregators"></a></h3><p>Adding AggregatorFactory objects is very similar to InputSource objects. They operate purely through Jackson and thus should just be additions to the Jackson modules returned by your DruidModule.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="adding-complex-metrics">Adding Complex Metrics<a href="#adding-complex-metrics" class="hash-link" aria-label="Direct link to Adding Complex Metrics" title="Direct link to Adding Complex Metrics"></a></h3><p>Adding ComplexMetrics is a little ugly in the current version. The method of getting at complex metrics is through registration with the <code>ComplexMetrics.registerSerde()</code> method. There is no special Guice stuff to get this working, just in your <code>configure(Binder)</code> method register the serialization/deserialization.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="adding-new-query-types">Adding new Query types<a href="#adding-new-query-types" class="hash-link" aria-label="Direct link to Adding new Query types" title="Direct link to Adding new Query types"></a></h3><p>Adding a new Query type requires the implementation of three interfaces.</p><ol><li><code>org.apache.druid.query.Query</code></li><li><code>org.apache.druid.query.QueryToolChest</code></li><li><code>org.apache.druid.query.QueryRunnerFactory</code></li></ol><p>Registering these uses the same general strategy as a deep storage mechanism does. You do something like</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">DruidBinders.queryToolChestBinder(binder)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .addBinding(SegmentMetadataQuery.class)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .to(SegmentMetadataQueryQueryToolChest.class);</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">DruidBinders.queryRunnerFactoryBinder(binder)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .addBinding(SegmentMetadataQuery.class)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .to(SegmentMetadataQueryRunnerFactory.class);</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>The first one binds the SegmentMetadataQueryQueryToolChest for usage when a SegmentMetadataQuery is used. The second one does the same thing but for the QueryRunnerFactory instead.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="adding-new-jersey-resources">Adding new Jersey resources<a href="#adding-new-jersey-resources" class="hash-link" aria-label="Direct link to Adding new Jersey resources" title="Direct link to Adding new Jersey resources"></a></h3><p>Adding new Jersey resources to a module requires calling the following code to bind the resource in the module:</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">Jerseys.addResource(binder, NewResource.class);</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="adding-a-new-password-provider-implementation">Adding a new Password Provider implementation<a href="#adding-a-new-password-provider-implementation" class="hash-link" aria-label="Direct link to Adding a new Password Provider implementation" title="Direct link to Adding a new Password Provider implementation"></a></h3><p>You will need to implement <code>org.apache.druid.metadata.PasswordProvider</code> interface. For every place where Druid uses PasswordProvider, a new instance of the implementation will be created,
thus make sure all the necessary information required for fetching each password is supplied during object instantiation.
In your implementation of <code>org.apache.druid.initialization.DruidModule</code>, <code>getJacksonModules</code> should look something like this -</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain"> return ImmutableList.of(</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> new SimpleModule(&quot;SomePasswordProviderModule&quot;)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .registerSubtypes(</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> new NamedType(SomePasswordProvider.class, &quot;some&quot;)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> )</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> );</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>where <code>SomePasswordProvider</code> is the implementation of <code>PasswordProvider</code> interface, you can have a look at <code>org.apache.druid.metadata.EnvironmentVariablePasswordProvider</code> for example.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="adding-a-new-dynamicconfigprovider-implementation">Adding a new DynamicConfigProvider implementation<a href="#adding-a-new-dynamicconfigprovider-implementation" class="hash-link" aria-label="Direct link to Adding a new DynamicConfigProvider implementation" title="Direct link to Adding a new DynamicConfigProvider implementation"></a></h3><p>You will need to implement <code>org.apache.druid.metadata.DynamicConfigProvider</code> interface. For every place where Druid uses DynamicConfigProvider, a new instance of the implementation will be created,
thus make sure all the necessary information required for fetching all information is supplied during object instantiation.
In your implementation of <code>org.apache.druid.initialization.DruidModule</code>, <code>getJacksonModules</code> should look something like this -</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain"> return ImmutableList.of(</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> new SimpleModule(&quot;SomeDynamicConfigProviderModule&quot;)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .registerSubtypes(</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> new NamedType(SomeDynamicConfigProvider.class, &quot;some&quot;)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> )</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> );</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>where <code>SomeDynamicConfigProvider</code> is the implementation of <code>DynamicConfigProvider</code> interface, you can have a look at <code>org.apache.druid.metadata.MapStringDynamicConfigProvider</code> for example.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="adding-a-transform-extension">Adding a Transform Extension<a href="#adding-a-transform-extension" class="hash-link" aria-label="Direct link to Adding a Transform Extension" title="Direct link to Adding a Transform Extension"></a></h3><p>To create a transform extension implement the <code>org.apache.druid.segment.transform.Transform</code> interface. You&#x27;ll need to install the <code>druid-processing</code> package to import <code>org.apache.druid.segment.transform</code>.</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">import com.fasterxml.jackson.annotation.JsonCreator;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">import com.fasterxml.jackson.annotation.JsonProperty;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">import org.apache.druid.segment.transform.RowFunction;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">import org.apache.druid.segment.transform.Transform;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">public class MyTransform implements Transform {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> private final String name;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> @JsonCreator</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> public MyTransform(</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> @JsonProperty(&quot;name&quot;) final String name</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> ) {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> this.name = name;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> @JsonProperty</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> @Override</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> public String getName() {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> return name;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> @Override</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> public RowFunction getRowFunction() {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> return new MyRowFunction();</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> static class MyRowFunction implements RowFunction {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> @Override</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> public Object eval(Row row) {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> return &quot;transformed-value&quot;;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Then register your transform as a Jackson module.</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">import com.fasterxml.jackson.databind.Module;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">import com.fasterxml.jackson.databind.jsontype.NamedModule;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">import com.fasterxml.jackson.databind.module.SimpleModule;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">import com.google.inject.Binder;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">import com.google.common.collect.ImmutableList;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">import org.apache.druid.initialization.DruidModule;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">public class MyTransformModule implements DruidModule {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> @Override</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> public List&lt;? extends Module&gt; getJacksonModules() {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> return return ImmutableList.of(</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> new SimpleModule(&quot;MyTransformModule&quot;).registerSubtypes(</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> new NamedType(MyTransform.class, &quot;my-transform&quot;)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> )</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> ):</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> @Override</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> public void configure(Binder binder) {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="adding-your-own-custom-pluggable-coordinator-duty">Adding your own custom pluggable Coordinator Duty<a href="#adding-your-own-custom-pluggable-coordinator-duty" class="hash-link" aria-label="Direct link to Adding your own custom pluggable Coordinator Duty" title="Direct link to Adding your own custom pluggable Coordinator Duty"></a></h3><p>The coordinator periodically runs jobs, so-called <code>CoordinatorDuty</code> which include loading new segments, segment balancing, etc.
Druid users can add custom pluggable coordinator duties, which are not part of Core Druid, without modifying any Core Druid classes.
Users can do this by writing their own custom coordinator duty implementing the interface <code>CoordinatorCustomDuty</code> and setting the <code>JsonTypeName</code>.
Next, users will need to register their custom coordinator as subtypes in their Module&#x27;s <code>DruidModule#getJacksonModules()</code>.
Once these steps are done, user will be able to load their custom coordinator duty using the following properties:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">druid.coordinator.dutyGroups=[&lt;GROUP_NAME_1&gt;, &lt;GROUP_NAME_2&gt;, ...]</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">druid.coordinator.&lt;GROUP_NAME_1&gt;.duties=[&lt;DUTY_NAME_MATCHING_JSON_TYPE_NAME_1&gt;, &lt;DUTY_NAME_MATCHING_JSON_TYPE_NAME_2&gt;, ...]</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">druid.coordinator.&lt;GROUP_NAME_1&gt;.period=&lt;GROUP_NAME_1_RUN_PERIOD&gt;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">druid.coordinator.&lt;GROUP_NAME_1&gt;.duty.&lt;DUTY_NAME_MATCHING_JSON_TYPE_NAME_1&gt;.&lt;SOME_CONFIG_1_KEY&gt;=&lt;SOME_CONFIG_1_VALUE&gt;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">druid.coordinator.&lt;GROUP_NAME_1&gt;.duty.&lt;DUTY_NAME_MATCHING_JSON_TYPE_NAME_1&gt;.&lt;SOME_CONFIG_2_KEY&gt;=&lt;SOME_CONFIG_2_VALUE&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>In the new system for pluggable Coordinator duties, similar to what coordinator already does today, the duties can be grouped together.
The duties will be grouped into multiple groups as per the elements in list <code>druid.coordinator.dutyGroups</code>.
All duties in the same group will have the same run period configured by <code>druid.coordinator.&lt;GROUP_NAME&gt;.period</code>.
Currently, there is a single thread running the duties sequentially for each group. </p><p>For example, see <code>KillSupervisorsCustomDuty</code> for a custom coordinator duty implementation and the <code>custom-coordinator-duties</code>
integration test group which loads <code>KillSupervisorsCustomDuty</code> using the configs set in <code>integration-tests/docker/environment-configs/test-groups/custom-coordinator-duties</code>.
This config file adds the configs below to enable a custom coordinator duty.</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">druid.coordinator.dutyGroups=[&quot;cleanupMetadata&quot;]</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">druid.coordinator.cleanupMetadata.duties=[&quot;killSupervisors&quot;]</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">druid.coordinator.cleanupMetadata.duty.killSupervisors.durationToRetain=PT0M</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">druid.coordinator.cleanupMetadata.period=PT10S</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>These configurations create a custom coordinator duty group called <code>cleanupMetadata</code> which runs a custom coordinator duty called <code>killSupervisors</code> every 10 seconds.
The custom coordinator duty <code>killSupervisors</code> also has a config called <code>durationToRetain</code> which is set to 0 minute.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="routing-data-through-a-http-proxy-for-your-extension">Routing data through a HTTP proxy for your extension<a href="#routing-data-through-a-http-proxy-for-your-extension" class="hash-link" aria-label="Direct link to Routing data through a HTTP proxy for your extension" title="Direct link to Routing data through a HTTP proxy for your extension"></a></h3><p>You can add the ability for the <code>HttpClient</code> of your extension to connect through an HTTP proxy. </p><p>To support proxy connection for your extension&#x27;s HTTP client:</p><ol><li>Add <code>HttpClientProxyConfig</code> as a <code>@JsonProperty</code> to the HTTP config class of your extension. </li><li>In the extension&#x27;s module class, add <code>HttpProxyConfig</code> config to <code>HttpClientConfig</code>.
For example, where <code>config</code> variable is the extension&#x27;s HTTP config from step 1:</li></ol><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">final HttpClientConfig.Builder builder = HttpClientConfig</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .builder()</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .withNumConnections(1)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .withReadTimeout(config.getReadTimeout().toStandardDuration())</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> .withHttpProxyConfig(config.getProxyConfig());</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="bundle-your-extension-with-all-the-other-druid-extensions">Bundle your extension with all the other Druid extensions<a href="#bundle-your-extension-with-all-the-other-druid-extensions" class="hash-link" aria-label="Direct link to Bundle your extension with all the other Druid extensions" title="Direct link to Bundle your extension with all the other Druid extensions"></a></h3><p>When you do <code>mvn install</code>, Druid extensions will be packaged within the Druid tarball and <code>extensions</code> directory, which are both underneath <code>distribution/target/</code>.</p><p>If you want your extension to be included, you can add your extension&#x27;s maven coordinate as an argument at
<a href="https://github.com/apache/druid/blob/master/distribution/pom.xml#L95" target="_blank" rel="noopener noreferrer">distribution/pom.xml</a></p><p>During <code>mvn install</code>, maven will install your extension to the local maven repository, and then call <a href="/docs/29.0.0/operations/pull-deps">pull-deps</a> to pull your extension from
there. In the end, you should see your extension underneath <code>distribution/target/extensions</code> and within Druid tarball.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="managing-dependencies">Managing dependencies<a href="#managing-dependencies" class="hash-link" aria-label="Direct link to Managing dependencies" title="Direct link to Managing dependencies"></a></h3><p>Managing library collisions can be daunting for extensions which draw in commonly used libraries. Here is a list of group IDs for libraries that are suggested to be specified with a <code>provided</code> scope to prevent collision with versions used in druid:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;org.apache.druid&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;com.metamx.druid&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;asm&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;org.ow2.asm&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;org.jboss.netty&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;com.google.guava&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;com.google.code.findbugs&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;com.google.protobuf&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;com.esotericsoftware.minlog&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;log4j&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;org.slf4j&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;commons-logging&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;org.eclipse.jetty&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;org.mortbay.jetty&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;com.sun.jersey&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;com.sun.jersey.contribs&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;common-beanutils&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;commons-codec&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;commons-lang&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;commons-cli&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;commons-io&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;javax.activation&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;org.apache.httpcomponents&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;org.apache.zookeeper&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;org.codehaus.jackson&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;com.fasterxml.jackson&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;com.fasterxml.jackson.core&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;com.fasterxml.jackson.dataformat&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;com.fasterxml.jackson.datatype&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;org.roaringbitmap&quot;,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&quot;net.java.dev.jets3t&quot;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>See the documentation in <code>org.apache.druid.cli.PullDependencies</code> for more information.</p></div></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Docs pages"><a class="pagination-nav__link pagination-nav__link--prev" href="/docs/29.0.0/development/overview"><div class="pagination-nav__sublabel">Previous</div><div class="pagination-nav__label">Developing on Druid</div></a><a class="pagination-nav__link pagination-nav__link--next" href="/docs/29.0.0/development/javascript"><div class="pagination-nav__sublabel">Next</div><div class="pagination-nav__label">JavaScript functionality</div></a></nav></div></div><div class="col col--3"><div class="tableOfContents_bqdL thin-scrollbar theme-doc-toc-desktop"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#writing-your-own-extensions" class="table-of-contents__link toc-highlight">Writing your own extensions</a><ul><li><a href="#creating-a-druid-module" class="table-of-contents__link toc-highlight">Creating a Druid Module</a></li><li><a href="#registering-your-druid-module" class="table-of-contents__link toc-highlight">Registering your Druid Module</a></li><li><a href="#adding-a-new-deep-storage-implementation" class="table-of-contents__link toc-highlight">Adding a new deep storage implementation</a></li><li><a href="#validating-your-deep-storage-implementation" class="table-of-contents__link toc-highlight">Validating your deep storage implementation</a></li><li><a href="#adding-support-for-a-new-input-source" class="table-of-contents__link toc-highlight">Adding support for a new input source</a></li><li><a href="#adding-support-for-a-new-data-format" class="table-of-contents__link toc-highlight">Adding support for a new data format</a></li><li><a href="#adding-aggregators" class="table-of-contents__link toc-highlight">Adding Aggregators</a></li><li><a href="#adding-complex-metrics" class="table-of-contents__link toc-highlight">Adding Complex Metrics</a></li><li><a href="#adding-new-query-types" class="table-of-contents__link toc-highlight">Adding new Query types</a></li><li><a href="#adding-new-jersey-resources" class="table-of-contents__link toc-highlight">Adding new Jersey resources</a></li><li><a href="#adding-a-new-password-provider-implementation" class="table-of-contents__link toc-highlight">Adding a new Password Provider implementation</a></li><li><a href="#adding-a-new-dynamicconfigprovider-implementation" class="table-of-contents__link toc-highlight">Adding a new DynamicConfigProvider implementation</a></li><li><a href="#adding-a-transform-extension" class="table-of-contents__link toc-highlight">Adding a Transform Extension</a></li><li><a href="#adding-your-own-custom-pluggable-coordinator-duty" class="table-of-contents__link toc-highlight">Adding your own custom pluggable Coordinator Duty</a></li><li><a href="#routing-data-through-a-http-proxy-for-your-extension" class="table-of-contents__link toc-highlight">Routing data through a HTTP proxy for your extension</a></li><li><a href="#bundle-your-extension-with-all-the-other-druid-extensions" class="table-of-contents__link toc-highlight">Bundle your extension with all the other Druid extensions</a></li><li><a href="#managing-dependencies" class="table-of-contents__link toc-highlight">Managing dependencies</a></li></ul></li></ul></div></div></div></div></main></div></div><footer class="footer"><div class="container container-fluid"><div class="footer__bottom text--center"><div class="margin-bottom--sm"><img src="/img/favicon.png" class="themedImage_ToTc themedImage--light_HNdA footer__logo"><img src="/img/favicon.png" class="themedImage_ToTc themedImage--dark_i4oU footer__logo"></div><div class="footer__copyright">Copyright © 2023 Apache Software Foundation. Except where otherwise noted, licensed under CC BY-SA 4.0. Apache Druid, Druid, and the Druid logo are either registered trademarks or trademarks of The Apache Software Foundation in the United States and other countries.</div></div></div></footer></div>
<script src="/assets/js/runtime~main.63d85568.js"></script>
<script src="/assets/js/main.7181d856.js"></script>
</body>
</html>