2016-07-13 91 views
15

Ho una funzione AWS Lambda, configurata con solo 128 MB di memoria, viene attivata da SNS (che è a sua volta attivata da S3) e scaricherà il file da S3.OutOfMemoryError durante la creazione di AmazonS3Client in Lambda

Nella mia funzione, ho il seguente:

public class LambdaHandler { 

    private final AmazonS3Client s3Client = new AmazonS3Client(); 

    public void gdeltHandler(SNSEvent event, Context context) { 
     System.out.println("Starting"); 
     System.out.println("Found " + eventFiles.size() + " event files"); 
    } 

Ho commentato fuori ed esclusa da questo post tutta la logica, perché sto ottenendo un OutOfMemoryError che ho isolato alla creazione del AmazonS3Client oggetto. Quando tengo fuori quell'oggetto, non ottengo l'errore. Il codice sopra riportato esita in OutOfMemoryError.

Ho assegnato 128 MB di memoria alla funzione, non è davvero sufficiente prendere semplicemente le credenziali e creare un'istanza dell'oggetto AmazonS3Client?

Ho provato dando il costruttore AmazonS3Client

new EnvironmentVariableCredentialsProvider() 

così come

new InstanceProfileCredentialsProvider() 

con risultati simili.

La creazione dell'oggetto AmazonS3Client richiede semplicemente più memoria?

Di seguito si riporta l'analisi dello stack:

Metaspace: java.lang.OutOfMemoryError java.lang.OutOfMemoryError: Metaspace at com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder.build(BeanDeserializerBuilder.java:347) at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:242) at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:143) at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:409) at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:358) at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:265) at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:245) at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:143) at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:439) at com.fasterxml.jackson.databind.ObjectReader._prefetchRootDeserializer(ObjectReader.java:1588) at com.fasterxml.jackson.databind.ObjectReader.(ObjectReader.java:185) at com.fasterxml.jackson.databind.ObjectMapper._newReader(ObjectMapper.java:558) at com.fasterxml.jackson.databind.ObjectMapper.reader(ObjectMapper.java:3108)

Quando provo fornire l'InstanceProfileCredentialsProvider o EnvironmentVariableCredentialsProvider, ottengo il seguente stack trace:

Exception in thread "main" java.lang.Error: java.lang.OutOfMemoryError: Metaspace at lambdainternal.AWSLambda.(AWSLambda.java:62) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:348) at lambdainternal.LambdaRTEntry.main(LambdaRTEntry.java:94) Caused by: java.lang.OutOfMemoryError: Metaspace at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:763) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) at java.net.URLClassLoader.access$100(URLClassLoader.java:73) at java.net.URLClassLoader$1.run(URLClassLoader.java:368) at java.net.URLClassLoader$1.run(URLClassLoader.java:362) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:361) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at lambdainternal.EventHandlerLoader$PojoMethodRequestHandler.makeRequestHandler(EventHandlerLoader.java:421) at lambdainternal.EventHandlerLoader.getTwoLengthHandler(EventHandlerLoader.java:777) at lambdainternal.EventHandlerLoader.getHandlerFromOverload(EventHandlerLoader.java:802) at lambdainternal.EventHandlerLoader.loadEventPojoHandler(EventHandlerLoader.java:888) at lambdainternal.EventHandlerLoader.loadEventHandler(EventHandlerLoader.java:740) at lambdainternal.AWSLambda.findUserMethodsImmediate(AWSLambda.java:126) at lambdainternal.AWSLambda.findUserMethods(AWSLambda.java:71) at lambdainternal.AWSLambda.startRuntime(AWSLambda.java:219) at lambdainternal.AWSLambda.(AWSLambda.java:60) ... 3 more START RequestId: 58837136-483e-11e6-9ed3-39246839616a Version: $LATEST END RequestId: 58837136-483e-11e6-9ed3-39246839616a REPORT RequestId: 58837136-483e-11e6-9ed3-39246839616a Duration: 15002.92 ms Billed Duration: 15000 ms Memory Size: 128 MB Max Memory Used: 50 MB
2016-07-12T14:40:28.048Z 58837136-483e-11e6-9ed3-39246839616a Task timed out after 15.00 seconds

EDIT 1 Se io aumentare la memoria allocata alla funzione fino a 192MB, funziona bene, anche se abbastanza stranamente, riporta solo 59MB di memoria nei log di cloudwatch. Sto semplicemente perdendo il resto della memoria?

+1

Hai trovato una soluzione per questo? Penso che la parte meta memoria sia sovraccarica a causa del caricamento di classe causato da Jackson. Il metaspace è una percentuale della memoria totale, quindi se si aumenta la memoria jm totale, il metaspace avrà più memoria con cui lavorare prima di generare un errore OutOfMemoryError. Sarebbe bello se fosse possibile aumentare semplicemente la parte metaspace della memoria. (-XX: MaxMetaspaceSize = 512m) Un'altra soluzione potrebbe essere quella di modificare Jackson se è possibile? Spiegazione di metaspace: https: // plumbr.eu/outofmemoryerror/metaspace –

+0

Nessuna soluzione di cui sono a conoscenza ... – Brooks

risposta

12

Ho osservato questo quando si utilizza AWS Java SDK all'interno della funzione Lambda. Sembrerebbe che quando si crea uno qualsiasi dei client AWS (Sync o Async) si possa uscire da Metaspace.

Credo che ciò sia dovuto a cose che il client Amazon esegue in fase di istanziazione, inclusa la creazione di AmazonHttpClient e il caricamento dinamico delle catene di gestori delle richieste (parte del metodo privato AmazonEc2Client#init()).

È possibile che l'utilizzo della memoria riportato sia per Heap stesso, ma potrebbe non includere Metaspace. Ci sono alcuni thread sui forum di AWS ma nessuna risposta da AWS in merito.

+1

Un'altra cosa che ho notato è che con l'allocazione di 192 MB, funziona perfettamente, ma mentre la prima esecuzione Lambda impiega più di 10 secondi per avviare e istanziare AmazonS3Client, le esecuzioni successive in un lasso di tempo di 1 ora richiedono meno di 1 secondo per creare un'istanza di AmazonS3Client ... supponendo che memorizzi l'autenticazione nella cache? Dopo 1 ora, tuttavia, sembra che la cache venga rilasciata e che l'autenticazione venga nuovamente eseguita nuovamente, maggiore di 10 secondi. – Brooks

+0

Sì, sembra che ci sia un meccanismo keep-alive sul posto. Non ho avuto il tempo di approfondire questo argomento, ma sto cercando un modo per ridurre l'impronta della memoria. Probabilmente rimuovendo alcune dipendenze che non vengono effettivamente utilizzate anche se non sono contrassegnate come "opzionali". – AlexejK

2

Prova ad aumentare la memoria allocata a lambda da 128 a 256 MB

+3

Si prega di notare la modifica apportata alla domanda originale. Aumentare la memoria lo fissò ma implorò una domanda più grande. – Brooks