CINXE.COM
L2 Cache | Ebean ORM
<!doctype html> <html lang="en"> <head> <title>L2 Cache | Ebean ORM</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="shortcut icon" href="/images/favicon.ico"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto|Source+Sans+Pro|Ubuntu&display=swap"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous"> <link rel="stylesheet" href="/css/reset3.css"> <link rel="stylesheet" href="/css/site3.css"> <link rel="stylesheet" href="/css/pygments3.css"> </head> <body> <div id="main"> <div id="banner"> <header> <nav id="top"> <h1 id="breadcrumb"> <a class="nav-logo" href="/"><img src="/images/logo-200.png" height="35"></a> <a href="/docs">Documentation</a><span class="sep"> / </span><a href="/docs/features/">Features</a><span class="sep"> / </span><span class="last">L2 Cache</span> </h1> <ul> <li><a onclick="toggleTheme();" title="switch dark light theme"><i class="fas fa-adjust"></i></a></li> </ul> </nav> </header> </div> <div class="grid grid-docs"> <aside> <nav class="side"> <ul> <li class="nav0 "> <a href="/docs/getting-started">Getting started</a> </li> <li class="nav0 "> <a href="/docs/intro">Introduction</a> </li> <li class="nav0 active"> <a class="active" href="/docs">Documentation</a> <ul> <li class="nav1 "> <a href="/docs/best-practice">Best practice</a> </li> <li class="nav1 "> <a href="/docs/query">Query</a> </li> <li class="nav1 "> <a href="/docs/persist">Persist</a> </li> <li class="nav1 "> <a href="/docs/transactions">Transactions</a> </li> <li class="nav1 "> <a href="/docs/mapping">Mapping</a> </li> <li class="nav1 "> <a href="/docs/ddl-generation">DDL & Migrations</a> </li> <li class="nav1 "> <a href="/docs/logging">Logging</a> </li> <li class="nav1 "> <a href="/docs/testing">Testing</a> </li> <li class="nav1 "> <a href="/docs/read-replicas">Read Replicas</a> </li> <li class="nav1 "> <a href="/docs/database">Database platforms</a> </li> <li class="nav1 "> <a href="/docs/multi-database">Multiple databases</a> </li> <li class="nav1 "> <a href="/docs/kotlin">Kotlin</a> </li> <li><a href="/docs/tuning">Tuning</a></li> <li class="nav1 active"> <a class="active" href="/docs/features">Features</a> <ul class="nav"> <li class="active"> <a class="active" href="/docs/features/l2cache">L2 Cache</a> <ul class="nav"> <li class="active"> <a class="active" href="/docs/features/l2cache">Overview</a> <ul class="nav nav-scroll"> <li><a href="#overview">Overview</a></li> <li><a href="#read-consistency">Eventual consistency</a></li> <li><a href="#invalidation">Invalidation</a></li> <li><a href="#near-cache">Near cache</a></li> <li><a href="#regions">Regions</a></li> <li><a href="#configuration">Configuration</a></li> </ul> </li> <li > <a href="/docs/features/l2cache/bean-cache">Bean caching</a> </li> <li > <a href="/docs/features/l2cache/query-cache">Query caching</a> </li> <li > <a href="/docs/features/l2cache/invalidation">Invalidation</a> </li> <li > <a href="/docs/features/l2cache/hazelcast">Hazelcast</a> </li> <li > <a href="/docs/features/l2cache/apache-ignite">Apache Ignite</a> </li> </ul> </li> <li > <a href="/docs/features/elasticsearch">Elasticsearch</a> </li> <li > <a href="/docs/features/json-in-db">@DbJson</a> </li> <li > <a href="/docs/features/softdelete">Soft Delete</a> </li> <li > <a href="/docs/features/encryption">Encryption</a> </li> <li > <a href="/docs/features/who">@WhoModified / @WhoCreated</a> </li> <li > <a href="/docs/features/history">SQL2011 History</a> </li> <li > <a href="/docs/features/changelog">ChangeLog</a> </li> <li > <a href="/docs/features/readauditing">Read auditing</a> </li> <li > <a href="/docs/features/eventlistening">Event Listening</a> </li> </ul> </li> </ul> </li> <li class="nav0 "> <a href="/support">Getting help</a> </li> <li class="nav0 "> <a target="_blank" href="/apidoc/13">API Javadoc</a> </li> <li class="nav0 "> <a href="/videos">Videos</a> </li> <li class="nav0 "> <a href="/docs/upgrading">Upgrading</a> </li> <li class="nav0 "> <a href="/releases">Releases</a> </li> </ul> </nav> </aside> <article> <form action="https://www.google.com/search" method="get" class="inline-form"> <input type="hidden" name="as_sitesearch" value="ebean.io"> <div id="page-search"> <div class="input-group"> <input class="frm" name="q" id="searchinput" type="text" placeholder="Search... (press 's' to focus)" data-placeholder-focus="Search... (use '↑', '↓' and '⏎' to select results)" data-placeholder-blur="Search... (press 's' to focus)" autocomplete="off"> <div class="input-group-btn"> <button class="frm" type="submit"><i class="fas fa-search"></i></button> </div> </div> <div id="page-search-results" style="display: none;"> <ul id="search-results-container" class="search-results"><li class=" active"><a href="/docs" title="Docs"><span style="color:#777;">Docs</span> Documentation </a></li><li class=""><small style="color:#999;">And 101 more...</small></li></ul> </div> </div> </form> <h2 id="overview">Overview</h2> <p> The goal of the L2 cache is to gain very significant performance improvement by not having to hit the database to read some information. </p> <p> For example, instead of performing a "find by id" or "find by unique key" query we can get Ebean to effectively lookup the result in the L2 cache. If we are using <em>near caching</em> then this might often result in an in-memory lookup into a local Map and not even require a remote call so be very fast. </p> <p> It is called the "Level 2 cache" because the persistence context is often referred to as the "Level 1 cache". </p> <p> The "L2 cache" has 2 main caches – "Bean caches" and "Query caches". </p> <h3>Bean Caches</h3> <p> Bean caches hold entity beans and are keyed by their <em>Id</em> values and optionally also by their <em>natural key</em>. The bean cache can be used with: </p> <ul> <li>Find by id(s)</li> <li>Find by natural key(s)</li> </ul> <h3>Query Caches</h3> <p> Query caches hold the results of queries (Lists, Sets, Maps of entity beans and Count) and are keyed by a hash of the query and its bind values. </p> <p> The entries in a query cache are invalidated by ANY change to the underlying table – insert, update or delete. This means that the query cache is generally only useful on entities that are relatively infrequently modified (typically "lookup tables" such as countries, currencies, status codes etc). </p> <p> The query cache can be used with: </p> <ul> <li>Find list/set/map queries</li> </ul> <h2 id="read-consistency">Eventual consistency</h2> <p> Ebean L2 caching uses <em>eventual consistency</em>. </p> <p> This means it operates a bit like "replication lag". Data returned from the L2 cache can be (milliseconds) out of date. Changes will be made to the cache in a relatively short time (milliseconds) but there isn't the guarantee of read consistency that our transactional database provides. </p> <p> With Ebean L2 caching there is no attempt to provide transactional read consistency. If part of an application needs transactional read consistency then we should <em>use the database</em> to do that. </p> <h2 id="invalidation">Invalidation</h2> <p> Ebean will automatically invalidate the cache when <a href="/docs/features/l2cache/invalidation#bean">beans are persisted</a> or <a href="/docs/features/l2cache/invalidation#table">update queries</a> are executed. </p> <p> In addition we can explicitly <a href="/docs/features/l2cache/invalidation#explicit">invalidate parts of the cache</a> programmatically. </p> <h4>Background notification</h4> <p> By default Ebean will perform L2 cache updates and invalidation in the background. If we want cache updates and invalidation to execute in the foreground then we need to set <code>notifyL2CacheInForeground</code> to true. </p> <h2 id="near-cache">Near cache</h2> <p> A cache that runs in the same process as Ebean is a <em>Near cache</em>. Performing a hit against a near cache is an in-memory Map lookup and does not go over the network. Hits against a near cache are very fast - it's effectively a local in-memory Map get(). </p> <p> Ebean L2 bean caches for Redis, Hazelcast and Ignite all have a Near cache option. When this is enabled then there is a local in-process cache that is used and hit first, and then only if there is a miss on the near cache does the request go to the remote Redis | Hazelcast | Ignite cache. </p> <p> For example, using ebean-redis with bean caching on Customer with near cache enabled: </p> <div class="syntax java"><div class="highlight"><pre><span></span><span class="nd">@Cache</span><span class="o">(</span><span class="nx">nearCache</span><span class="o">=</span><span class="kc">true</span><span class="o">)</span> <span class="nd">@Entity</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">Customer</span> </pre></div> </div> <ul> <li>First hit the local in-memory near cache, if hit return the Customer</li> <li>Second, hit the remote Redis cache, if hit return the Customer</li> <li>Third, query the database, return the Customer, also loading Redis cache and near cache</li> </ul> <p> Note that all Query caches are implemented as near cache only. </p> <h2 id="regions">Regions</h2> <p> We use "Regions" to group caches so that we can enable and disable L2 caching by "region groups". </p> <p> This provides a mechanism where we can "feature toggle" / enable L2 caching by named regions. </p> <p> Let's say our application rolls out, and it uses L2 caching for Product (and we call that region "product-region"). Now, we develop further and as part of that want to use L2 caching for Price but, we don't yet want to enable that in all environments (e.g. we don't want it enabled in production). In dev and test we might enable the l2 cache regions "product-region,price-region" but in production only enable "product-region". </p> <div class="syntax java"><div class="highlight"><pre><span></span><span class="nd">@Entity</span> <span class="nd">@Cache</span><span class="o">(</span><span class="nx">region</span> <span class="o">=</span> <span class="s">"product-region"</span><span class="o">)</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">Product</span> </pre></div> </div> <h4>enabledL2Regions</h4> <div class="syntax properties"><div class="highlight"><pre><span></span><span class="na">ebean.enabledL2Regions</span><span class="o">=</span><span class="s">product-region,price-region</span> </pre></div> </div> <h2 id="configuration">Configuration</h2> <p> Configuration options that can be set via <code>DatabaseConfig</code>. </p> <table class="table"> <tr> <th>Parameter</th> <th>Default</th> <th>Description</th> </tr> <tr> <td>disableL2Cache</td> <td>false</td> <td>Set to true to globally disable L2 caching</td> </tr> <tr> <td>enabledL2Regions</td> <td></td> <td>Set the enabled L2 cache regions (comma delimited). By default all regions are enabled.</td> </tr> <tr> <td>localOnlyL2Cache</td> <td>false</td> <td>Set to true to effectively disable L2 cache plugins (like ebean-redis, ebean-hazelcast)</td> </tr> <tr> <td>notifyL2CacheInForeground</td> <td>false</td> <td>Set to true for cache notifications and invalidation to run in the foreground. Generally we want to perform L2 cache notification in the background and not impact the performance of executing transactions. </td> </tr> <tr> <td></td> <td></td> <td></td> </tr> <!-- </table>--> <!-- <table class="table">--> <tr> <td>cacheMaxSize</td> <td>10000</td> <td></td> </tr> <tr> <td>cacheMaxIdleTime</td> <td>10mins</td> <td></td> </tr> <tr> <td>cacheMaxTimeToLive</td> <td>6hrs</td> <td></td> </tr> <tr> <td>queryCacheMaxSize</td> <td>1000</td> <td></td> </tr> <tr> <td>queryCacheMaxIdleTime</td> <td>10mins</td> <td></td> </tr> <tr> <td>queryCacheMaxTimeToLive</td> <td>6hrs</td> <td></td> </tr> </table> </article> </div> </div> <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script> <script src="/js/site3.js"></script> <script src="/js/search3.js"></script> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-75181644-1"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-75181644-1'); </script> </body> </html>