Transcript
Page 1: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Senior Software Engineer, Etsy.com

LIVING WITH GARBAGEGregg Donovan

Page 2: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

3.5 Years Solr & Lucene at Etsy.com

3 years Solr & Lucene at TheLadders.com

Page 3: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 4: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

8+ million members

Page 5: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

20 million items

Page 6: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

800k+ active sellers

Page 7: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

8+ billion pageviews per month

Page 8: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 9: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 10: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 11: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 12: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 13: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 14: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 15: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

CodeAsCraft.etsy.com

Page 16: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Understanding GCMonitoring GC

Debugging Memory LeaksDesign for Partial Availability

Page 17: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 18: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

public class BuzzwordDetector { static String[] prefixes = { "synergy", "win-win" }; static String[] myArgs = { "clown synergy", "gorilla win-wins", "whamee" };

public static void main(String[] args) { args = myArgs;

int buzzwords = 0; for (int i = 0; i < args.length; i++) { String lc = args[i].toLowerCase(); for (int j = 0; j < prefixes.length; j++) { if (lc.contains(prefixes[j])) { buzzwords++; } } } System.out.println("Found " + buzzwords + " buzzwords"); }}

Page 19: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

New(): ref <- allocate() if ref = null /* Heap is full */ collect() ref <- allocate() if ref = null /* Heap is still full */ error "Out of memory" return ref atomic collect(): markFromRoots() sweep(HeapStart, HeapEnd)

From Garbage Collection Handbook

Page 20: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

markFromRoots(): initialise(worklist) for each fld in Roots ref <- *fld if ref != null && not isMarked(ref) setMarked(ref) add(worklist, ref) mark() initialise(worklist): worklist <- empty mark(): while not isEmpty(worklist) ref <- remove(worklist) /* ref is marked */ for each fld in Pointers(ref) child <- *fld if (child != null && not isMarked(child) setMarked(child) add(worklist, child)

From Garbage Collection Handbook

Page 21: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Trivia: Who invented the first GC and Mark-and-Sweep?

Page 22: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Weak Generational Hypothesis

Page 23: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Where do objects in common Solr application live?

AtomicReaderContext?

SolrIndexSearcher?

SolrRequest?

Page 24: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

GC Terminology:Concurrent vs Parallel

Page 25: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

JVM Collectors

Page 26: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Serial

Page 27: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Trivia: How does System.identityHashCode() work?

Page 28: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Throughput

Page 29: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

CMS

Page 30: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Garbage First (G1)

Page 31: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Continuously Concurrent Compacting Collector (C4)

Page 32: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

IBM, Dalvik, etc.?

Page 33: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Why Throughput?

Page 34: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Monitoring

Page 35: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

GC time per Solr request

Page 36: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

...import java.lang.management.*;...

public static long getCollectionTime() { long collectionTime = 0; for (GarbageCollectorMXBean mbean : ManagementFactory.getGarbageCollectorMXBeans()) { collectionTime += mbean.getCollectionTime(); } return collectionTime; }

Available via JMX

Page 37: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Visual GC

Page 38: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 39: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

export GC_DEBUG="-verbose:gc \-XX:+PrintGCDateStamps \-XX:+PrintHeapAtGC \-XX:+PrintGCApplicationStoppedTime \-XX:+PrintGCApplicationConcurrentTime \-XX:+PrintAdaptiveSizePolicy \-XX:AdaptiveSizePolicyOutputInterval=1 \-XX:+PrintTenuringDistribution \-XX:+PrintGCDetails \-XX:+PrintCommandLineFlags \-XX:+PrintSafepointStatistics \-Xloggc:/var/log/search/gc.log"

Page 40: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

2013-04-08T20:14:00.162+0000: 4197.791: [Full GCAdaptiveSizeStart: 4206.559 collection: 213 PSAdaptiveSizePolicy::compute_generation_free_space limits: desired_promo_size: 9927789154 promo_limit: 8321564672 free_in_old_gen: 4096 max_old_gen_size: 22190686208 avg_old_live: 22190682112AdaptiveSizePolicy::compute_generation_free_space limits: desired_eden_size: 9712028790 old_eden_size: 8321564672 eden_limit: 8321564672 cur_eden: 8321564672 max_eden_size: 8321564672 avg_young_live: 7340911616AdaptiveSizePolicy::compute_generation_free_space: gc time limit gc_cost: 1.000000 GCTimeLimit: 98PSAdaptiveSizePolicy::compute_generation_free_space: costs minor_time: 0.167092 major_cost: 0.965075 mutator_cost: 0.000000 throughput_goal: 0.990000 live_space: 29859940352 free_space: 16643129344 old_promo_size: 8321564672 old_eden_size: 8321564672 desired_promo_size: 8321564672 desired_eden_size: 8321564672AdaptiveSizeStop: collection: 213 [PSYoungGen: 8126528K->7599356K(9480896K)] [ParOldGen: 21670588K->21670588K(21670592K)] 29797116K->29269944K(31151488K) [PSPermGen: 58516K->58512K(65536K)], 8.7690670 secs] [Times: user=137.36 sys=0.03, real=8.77 secs] Heap after GC invocations=213 (full 210): PSYoungGen total 9480896K, used 7599356K [0x00007fee47ab0000, 0x00007ff0dd000000, 0x00007ff0dd000000) eden space 8126528K, 93% used [0x00007fee47ab0000,0x00007ff0177ef080,0x00007ff037ac0000) from space 1354368K, 0% used [0x00007ff037ac0000,0x00007ff037ac0000,0x00007ff08a560000) to space 1354368K, 0% used [0x00007ff08a560000,0x00007ff08a560000,0x00007ff0dd000000) ParOldGen total 21670592K, used 21670588K [0x00007fe91d000000, 0x00007fee47ab0000, 0x00007fee47ab0000) object space 21670592K, 99% used [0x00007fe91d000000,0x00007fee47aaf0e0,0x00007fee47ab0000) PSPermGen total 65536K, used 58512K [0x00007fe915000000, 0x00007fe919000000, 0x00007fe91d000000) object space 65536K, 89% used [0x00007fe915000000,0x00007fe918924130,0x00007fe919000000)}

Page 41: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

GC Log Analyzers?

GCHisto

GCViewer

garbagecat

Page 42: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Graphing with Logster

github.com/etsy/logster

Page 43: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 44: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

GC Dashboardgithub.com/etsy/dashboard

Page 45: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 46: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

YourKit.com

Page 47: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Designing for Partial Availability

Page 48: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

JVMTI GC Hook?

Page 49: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

How can a client ignore GC-ing hosts?

Page 50: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Server lies to clients about availability

TCP socket receive buffer

TCP write buffer

Page 51: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

“Banner” protocol1. Connect via TCP

2. Wait ~1-10ms

3. Either receive magic four byte header or try another host

4. Only send query after receiving header from server

Page 52: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

0xC0DEA5CF

Page 53: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

What if GC happens mid-request?

Page 54: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Backup requests

Page 55: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Jeff Dean: Achieving Rapid Response Time in Large

Online Services

Page 56: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Solr sharding?

Right now, only as fast as the slowest shard.

Page 57: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

“Make a reliable whole out of unreliable parts.”

Page 58: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Memory Leaks

Page 59: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Solr API hooks for custom code

QParserPlugin SearchComponent

SolrRequestHandler SolrEventListener

SolrCache ValueSourceParser

etc.FieldType

Page 60: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

PSA: Are you sure you need custom code?

Page 61: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

CoreContainer#getCore()

RefCounted<SolrIndexSearcher>

Page 62: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

SolrIndexSearcher generation marking with YourKit triggers

Page 63: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 64: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Miscellaneous Topics

Page 65: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

System.gc()?

Page 66: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

-XX:+UseCompressedOops

Page 67: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

-XX:+UseNUMA

Page 68: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Paging

Page 69: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

#!/usr/bin/env bash

# This script is designed to be run every minute by cron.

host=$(hostname -s)

psout=$(ps h -p `cat /var/run/etsy-search.pid` -o min_flt,maj_flt 2>/dev/null)min_flt=$(echo $psout | awk '{print $1}') # minor page faultsmaj_flt=$(echo $psout | awk '{print $2}') # major page faults

epoch_s=$(date +%s)

echo -e "search_memstats.$host.etsy-search.min_flt\t${min_flt:-0}\t$epoch_s" | nc graphite.etsycorp.com 2003echo -e "search_memstats.$host.etsy-search.maj_flt\t${maj_flt:-0}\t$epoch_s" | nc graphite.etsycorp.com 2003

Page 70: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Solution 1: Buy more RAM

Ideally enough RAM to:Keep index in OS file buffersAND ensure no paging of VM memory AND whatever else happens on the box

~$5-10/GB

Page 71: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

echo “0” > /proc/sys/vm/swappiness

Page 72: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

mlock()/mlockall()

Page 73: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

echo “-17” > /proc/$PID/oom_adj

Mercy from the OOM Killer

Page 74: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Huge Pages

Page 75: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

-XX:+AlwaysPreTouch

Page 76: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Possible Future Directions

Page 77: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Many small VMs instead of one large VM

microsharding

Page 78: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

In-memory Lucene codecs

I.e. custom DirectPostingsFormat

Page 79: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Off-heap memory with sun.misc.Unsafe?

Page 80: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Try G1 again

Page 81: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Try C4 again

Page 82: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Resources

Page 83: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

gchandbook.org

Page 84: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013
Page 85: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

bit.ly/mmgcb

Mark Miller’s GC Bootcamp

Page 86: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

bit.ly/giltene

Gil Tene: Understanding Java Garbage Collection

Page 87: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

bit.ly/cpumemory

Ulrich Drepper: What Every Programmer Should Know About Memory

Page 88: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

github.com/pingtimeout/jvm-options

Page 89: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Read the JVM Source(Not as scary as it sounds.)

hg.openjdk.java.net/jdk7/jdk7

Page 90: Living with Garbage by Gregg Donovan at LuceneSolr Revolution 2013

Mechanical Sympathy Google Group

bit.ly/mechsym