이것도 꽤 고생했다

 

프로젝트 크기가 커지면서 테스트케이스들이 많아지고 어느순간부터 OOM 발생 ..

 

여러 문서들에서는 

gradle.properties에서는 아래와 같은 형태로 메모리를 늘리면 된다

org.gradle.jvmargs=-Xmx2g

 

라고하는데

나의 경우는 제대로 잘 동작하지 않았다 <- 사실 이 문제를 아는데까지가 굉장히 오래걸렸음

 

결론부터 말하면 gradle.properties는 gradle daemon과 관련된 설정이다

아래 jps -lv 명령어를 사용하면 실제로 org.gradle.launcher.daemon.bootstrap.GradleDaemon <- 이놈들의 heap size 설정만 변경된것을 확인할 수 있다

jps -lv

2634 org.gradle.launcher.daemon.bootstrap.GradleDaemon ... -Xmx2g
16186 org.gradle.launcher.GradleMain ... -Xmx64m -Xms64m 
13051 org.gradle.launcher.daemon.bootstrap.GradleDaemon ... -Xmx2g
16271 worker.org.gradle.process.internal.worker.GradleWorkerMain -Xmx512m
...

 

하지만 실제 build를 수행하는애들은 worker인게 문제!!!

 

실제로 문서에는 아래와 같이 되어있다

https://docs.gradle.org/current/userguide/upgrading_version_4.html#rel5.0:default_memory_settings

All workers, including compilers and test executors, now start with 512MB of heap. The previous default was 1/4th of physical memory. Large projects may have to increase this setting on the relevant tasks, e.g. JavaCompile or Test

나랑 비슷한 질문을 한 사람도 있다 https://github.com/gradle/gradle/issues/8166

 

Default xmx changed before and after 5.0 for test executor? · Issue #8166 · gradle/gradle

When upgrading gradle from 4.10.2 to 5.1 (only variable, no other plugin changes), we observed some tests failing due to OOM in 5.1 which passed in 4.10.2 without any issue. With further investigat...

github.com

 

 

따라서 build.gradle에는 아래와 같은 스크립트가 추가되어야한다

test {
  maxHeapSize = "1024m" //원하는 만큼 변경
}

위에서 설정한 heapSize가 실제로 gradle build 명령어에서 사용하는 heap size를 수정하게 해준다!

 

멀티프로젝트라면 아래와 같이 하면 된다

subprojects {
  test {
    maxHeapSize = "1024m" //원하는 만큼 변경
  }
  
  ...
}

 

쩝 ..써놓고 보니까 별거없어보임 ㅠ

 

오랜만쓰 ... 

 

break 잡고 디버깅모드로 돌리는데 ide가 급격히 느려질때는

 

자신이 코드에 Method Breakpoint를 잡았는지, Line Breakpoint를 잡았는지 확인할 것.

 

맥 기준으로 Shift + Command + F8 을 누르면 ide에 적용된 모든 breakpoint를 확인할 수 있는데

 

만약 Java Method Breakpoints에 브레이크가 걸려있다면 Line Breakpoint로 바꾸면 매우 빨라짐!!

 

 

 

https://intellij-support.jetbrains.com/hc/en-us/articles/206544799

 

Java: slow performance or hangups when starting debugger and stepping

Debugger performance can be affected by the following: Method breakpoints will slow down debugger a lot because of the JVM design, they are expensive to evaluate. Remove method breakpoints and co...

intellij-support.jetbrains.com

 

비슷한 내용으로 검색하면 많은 글들에서 /etc/security/limits.conf 를 수정하라는 글들이 있었는데

 

※급하게 테스트가 필요할 경우!

 

ES 7.x 버전에서 아래와 같이 해결했다.

 

 

 

elasticsearch.yml에 아래내용 참고해서 추가

network.host: 0.0.0.0
http.port: 9200
transport.host: localhost
transport.tcp.port: 9300

 

transport.host, transport.tcp.port를 추가해서 다시 재기동하니까 잘 됐다.

 

 

 

 

 

stackoverflow.com/questions/46771233/max-file-descriptors-for-elasticsearch-process-is-too-low

 

max file descriptors for elasticsearch process is too low

Cannot resolve these problems: [2017-10-16T13:54:23,381][WARN ][o.e.b.BootstrapChecks ] [node-1] max file descriptors [65000] for elasticsearch process is too low, increase to at least [65536] [

stackoverflow.com

 

 

jpa, spring batch meta table들을 활용할때

 

h2 db를 임의로 초기화하고싶을때가 있다. 

 

근데 truncate할때 fk 등등의 제약조건 걸려서 못할때 (개빡침) 하는 방법

 

 

SET REFERENTIAL_INTEGRITY FALSE --제약조건 무효화

SHOW TABLES

TRUNCATE TABLE {TABLE_NAME}

SET REFERENTIAL_INTEGRITY TRUE --제약조건 재설정

 

 

 

 

stackoverflow.com/questions/27045568/h2-how-to-truncate-all-tables

 

H2 - How to truncate all tables?

I assume there is a way of doing this from code, at least some good workaround. Please consider that I do not want to delete all tables (I've seen this command), just to delete rows from them but to

stackoverflow.com

 

 

 

 

그냥 뭔가 조금 이상했는데 이유는 잘 모르겠다 ..

 

 

querydsl 4.1.4버전을 사용중이였는데 쿼리가 나가는 부분에 UnsupportedOperationException 에러가 발생했다.

 

추적을 좀 해보고싶었는데 구글링해도 도움이 될만한 내용은 찾지 못했다

 

 

일단 querydsl 버전을 4.1.4 -> 4.4.0(20201026 최신버전)  으로 바꾸니까 정상 동작했다.

 

간단하게 디버깅해봤을때

 

com.querydsl.jpa.JPQLSerializer 내부에 아래와 같은 함수가 있는데

    public void visitConstant(Object constant) {
        boolean wrap;
        if (this.inCaseOperation && this.templates.isCaseWithLiterals()) {
            if (constant instanceof Collection) {
                this.append("(");
                wrap = true;

                for(Iterator var5 = ((Collection)constant).iterator(); var5.hasNext(); wrap = false) {
                    Object o = var5.next();
                    if (!wrap) {
                        this.append(", ");
                    }

                    this.visitLiteral(o);
                }

                this.append(")");
            } else {
                this.visitLiteral(constant);
            }
        } else {
            wrap = this.templates.wrapConstant(constant);
            if (wrap) {
                this.append("(");
            }

            this.append("?");
            if (!this.getConstantToLabel().containsKey(constant)) {
                String constLabel = String.valueOf(this.getConstantToLabel().size() + 1);
                this.getConstantToLabel().put(constant, constLabel); // <-- 요기
                this.append(constLabel);
            } else {
                this.append((String)this.getConstantToLabel().get(constant));
            }

            if (wrap) {
                this.append(")");
            }
        }

    }

아래부분에 this.getConstantToLabel().put(constant, constLabel); 로 put을 사용하는데 여기서 사용하는 getConstantToLabel()이 리턴하는 자료구조가 UnmodifiableMap이다.

 

참고로 UnmodifiableMap은 put을 구현하지 않았기 때문에 호출시 UnsupportedOperationException 을리턴하는게당연하다.

 

 

버전을 4.4.0으로 올려서 확인해보면 같은 부분이라도 조금 다른걸 확인할 수 있다.

            this.append("?");
            if (!this.getConstantToAllLabels().containsKey(constant)) {
                Integer constLabel = this.getConstantToNumberedLabel().size() + 1;
                this.getConstantToNumberedLabel().put(constant, constLabel);
                this.append(constLabel.toString());
            } else {
                this.append((String)this.getConstantToAllLabels().get(constant));
            }

getConstantToLabel() -> getConstantToNumberedLabel() 으로 변경된 것을 확인할 수 있는데 실제로 getConstantToNumberedLabel() 가 리턴하는 자료구조는 HashMap이다. 고로 정상동작한다.

 

 

내가 api를 제대로 안본것도 있겠지만 왜 4.1.4 에서는 UnmodifiableMap을 리턴하게 해놨을지 ...

 

 

 

 

 

-결론

버전 올리면 된다.

 

별도로 h2 서버를 띄우지 않고

 

멀티 커넥션을 구성할 수 있는 방법이다

 

spring:
  datasource:
    url: "jdbc:h2:~/e-commerce-dev;AUTO_SERVER=TRUE"

 

 

다음과 같이 AUTO_SERVER=TRUE 옵션을 붙여주면 된다.

 

 

 

 

 

 

 

https://stackoverflow.com/questions/12401991/h2-database-multiple-connections

 

H2 Database multiple connections

I have the following issue: Two instances of an application on two different systems should share a small database. The main problem is that both systems can only exchange data through a network-fo...

stackoverflow.com

 

이글은 Java(Spring) 개발자에게만 유용할 수 있습니다.

 

Redis에서 나같이 더미데이터를 넣어서 성능테스트를 해볼사람이 있을 수 있다..

 

Redis는 mass insert라는 api를 제공해서 데이터를 빠르게 넣을 수 있다

 

근데이게 자기네는 쉬운 api라고 소개하는데 대충 모양이

 

 

"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n"

이런모양이다.

 

실제로도 그렇게 어려워보이진 않았지만 굳이 저걸 쓰고싶진 않았다..

 

자세한 정보는 아래에서 확인할 수 있다.

 

https://redis.io/topics/mass-insert

 

Redis Mass Insertion – Redis

*Redis Mass Insertion Sometimes Redis instances need to be loaded with a big amount of preexisting or user generated data in a short amount of time, so that millions of keys will be created as fast as possible. This is called a mass insertion, and the goal

redis.io

 

그래서 찾아낸 방법은 Jedis Client를 사용하는 방법이다.

 

Jedis Client는 pipeline api를 제공해주는데 사용방법은 아래와 같다

 

        try (Jedis jedis = new Jedis(redisHost, redisPort)) {
            Pipeline pipeline = jedis.pipelined();
            for (int i = 0; i < 7000000; i++) {

                HashMap<String, String> hashMap = new HashMap<>();
                hashMap.put("name", "p" + i);

                hashMap.put("score", String.valueOf(i * (int)(Math.random() * 100) + 1));

                pipeline.zadd(KEY,  Double.parseDouble(hashMap.get("score")), hashMap.get("name"));
                pipeline.hset(KEY + ":" + hashMap.get("name"), hashMap);
            }

            pipeline.sync();
        }

 

 

 

이방법을 사용하면 약 700만개의 데이터가 1~2분 내외로 들어간다.

 

진짜 너무좋다 ..

 

메모리 생각 안하고 데이터를 꾸겨넣다보면 메모리가 금방 터지니 주의해야한다.

 

 

 

 

 

https://stackoverflow.com/questions/30728409/how-to-do-mass-insertion-in-redis-using-java

 

How to do Mass insertion in Redis using JAVA?

Hi I need to do multiple insertions of the form SADD key value I have the key value pair and needed to know how to perform mass insertions using JAVA . I have written a file in the Redis Protoco...

stackoverflow.com

 

 

ps 

 

lettuce도 비슷한 api가 있다!

 

https://github.com/lettuce-io/lettuce-core/wiki/Pipelining-and-command-flushing

 

lettuce-io/lettuce-core

Advanced Java Redis client for thread-safe sync, async, and reactive usage. Supports Cluster, Sentinel, Pipelining, and codecs. - lettuce-io/lettuce-core

github.com

 

lettuce는 비동기라그런지 뭔가 복잡하다

 

단발로 끝낼꺼면 jedis를 쓰자

 

 

아... 진짜 반나절 넘게 삽질했다

 

로그 설정좀 제대로 하고 할껄 ...ㅜㅜㅜ

 

class is not in the trusted packages 에러는 아마 intellij에서 producer랑 consumer를 모듈로 각각분리해서

 

entity도 개별적으로 존재? 하는 상황일 것이다 (consumer와 producer의 entity의 package fullname이 다를경우)

 

예를들어 producer쪽 entity의 package를 포함한 fullname이 zzz.xxx.producer.MyEntity 이고

 

consumer쪽 entity의 package를 포함한 fullname이 zzz.xxx.consumer.MyEntity 라면

 

consumer쪽의 JsonDeserializer에서 class is not in the trusted packages 에러를 뿜뿜할 것이다.

 

왜냐면 직렬화, 역직렬화에서는 package 이름까지 포함하기때문이다 ...

 

해결은 JsonDeserializer에 addTrustedPackages() 메서드를 수정하면 된다.

 

    @Bean
    public ConsumerFactory<String, GCMPushEntity> pushEntityConsumerFactory() {
        JsonDeserializer<GCMPushEntity> deserializer = new JsonDeserializer<>(GCMPushEntity.class);
        deserializer.setRemoveTypeHeaders(false);
        deserializer.addTrustedPackages("*");
        deserializer.setUseTypeMapperForKey(true);

        Map<String, Object> props = new HashMap<>();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, deserializer);
        return new DefaultKafkaConsumerFactory<>(
                props,
                new StringDeserializer(),
                deserializer);
    }

 

진짜 개삽질했다...

 

 

 

https://stackoverflow.com/questions/51688924/spring-kafka-the-class-is-not-in-the-trusted-packages

 

Spring Kafka The class is not in the trusted packages

In my Spring Boot/Kafka application before the library update, I used the following class org.telegram.telegrambots.api.objects.Update in order to post messages to the Kafka topic. Right now I use ...

stackoverflow.com

 

+ Recent posts