Inertia

nice metaphor for sw architecture

최근에 교육을 받았는데 sw architecture에 관해서 기가 막히는 비유라 생각이 들어서 정리해보려 한다.

다들 잘 아는 피카소 그림중에 아래와 같은 2개의 그림이 있다.

alt text
alt text

피카소의 첫번째 그림은 자신의 연인을 여러 각도에서 본 모습을 그림으로 옮겨 놓아서 언뜻 보면 어떤 그림인지 알아보기 힘들게 되어 있다. 반면에 두번째 그림은 비교적 악기를 연주하는 여자를 그린것임을 직관적으로 알 수 있다.

이렇듯 sw란 것은 작성하는 사람에 따라서 같은 내용도 자유도가 높기 때문에 다양하게 표현될 수 있고 나중에 완성한 것을 다른 사람이 봤을때는 알아보기 어렵다는 것이 꼭 소프트웨어의 속성과 닮아 있다고 비유를 한 것이다.

SW architect의 역할은 다른 사람이 봤을때도 한사람이 그린것처럼 쉽게 만드는 것이라는 비유도 참 적절한 비유가 아닌가 하는 생각이 든다.

implement custom jacoco filter

Jacoco is popular coverage tool for Java worlds. It provides various options to include or exclude specific classes.

My special requirement is to generate exact coverage report for public functions of non UI classes. Jacoco doesn’t support that functionality. so I want to share my experiences here because I couldn’t find any solution for this.

The first thing I found was filters. If I can implement my own filter, then that’s all I need. Let’s analyze it more in source code level. After some time, I’ve found Filter class! It has all required filters for proper coverage reporting.

Next is implementing my own filter. Let’s reference already existing source code like AnnotationGeneratedFilter.java. Its main functionality is excludding functions annotated by having ‘Generated’ string.

I forked jacoco project and created _customFilter branch based on 0.8.3 tag and implemented WhiteListFilter and its testcase.

And I created binary using the following commands. jar files are located in jacoco/target/ folder.

mvn clean package -DskipTests=True

last thing you have to do is let gradle use those built jar files. copy those jar files to <root>/lib directory. and change your build.gradle file like the followings.

1
2
3
4
5
6
7
8
9
10
11
12
13

dependencies {
// override jacoco jars
jacocoAgent files("$rootProject.projectDir/lib/org.jacoco.agent-0.8.3.201904130250.jar")
jacocoAnt files("$rootProject.projectDir/lib/org.jacoco.ant-0.8.3.201904130250.jar",
"$rootProject.projectDir/lib/org.jacoco.core-0.8.3.201904130250.jar",
"$rootProject.projectDir/lib/org.jacoco.report-0.8.3.201904130250.jar",
"$rootProject.projectDir/lib/asm-7.0.jar",
"$rootProject.projectDir/lib/asm-tree-7.0.jar",
"$rootProject.projectDir/lib/asm-commons-7.0.jar"
)

}

then last thing you have to is annotate your target class using @TestRequired class.

1
2
3
4
5
6
7
8
9
10
@TestRequired
class MyClass {
public doSomething(String str) {
// this function will be included in coverage report.
}

private doSomethingElse(String str) {
// this function will NOT be included in coverage report.
}
}

Strong views, weakly Held

회사를 다니다 보면 자기 주장이 강한 사람, 커뮤니케이션이 어려운 사람들을 만나는데 이런 사람과는 협업하는 것이 고역이다. 헌데 어느날 블로그에서 이런 글을 봤는데 이런것을 가르키는 단어가 있는것이 아닌가. 너무 좋은 표현이라 생각해서 정리해 본다.

strong views, weakly held

  • 언제든 나의 주장보다 나은 이유가 있는 주장이 있다면 그것을 채택하는 것.
  • A, B 선택지가 있을때 내가 A를 선택했다면, A여야 하는 이유에 대해 리서치를 하지말고 B여야 하는 이유에 대해서 리서치를 해보고 판단을 내리면 더 나은 결정을 내릴 확률이 높다는 것.
    기존은 A/B 선택지가 있을때 자기의견이 맞는 이유만 찾기 때문에 기존의 틀?에 갖혀서 잘못된 결정을 내리기 쉽기 때문이다.

graph db

graph db에 대해서 한번 정리. AWS에서는 Neptune이 있고, 오픈 소스로는 Neo4j

why graph db ?

RDBMS 에서 relationship은 JOIN으로 표현되는데, 이런 relationship이 생길때 마다 JOIN이 반복되는데, 이게 어느 정도 이상의 복잡한 relation을 쿼리 해야 한다면 그만큼의 JOIN을 많이 해야 하고 테이블도 많아져야 한다. 이런 RDBMS의 pain point를 해결한것이 graph DB이다. graph에서 relation은 하나의 링크에 지나지 않으므로 같은 쿼리를 RDB와 graph로 비교해보면 그래프의 우위가 확실히 있다.

~~social network을 생각해보자. 사람과의 follower 관계를 쿼리 해야 하는데, darren의 follower중에 rachel의 follwer중 중복되는 사람을 찾는 다고 생각해보자. 그러면 RDBMS의 경우는 쿼리가 이렇게 될 것 같다. ~~

1
select * from follower D JOIN follower R WHERE D.user='darren' and R

reference

how to debug EMR spark job

aws EMR에서 띄운 spark를 디버깅 하는 팁..

  1. 일단 debuggin option을 켜고, emr을 론치할때 LogUri로 s3 path를 지
    정함
  2. 그러면 /logs/<cluster_id>/containers/application_/container_xx_xx_ 이런 형식으로 로그가 저장된다. 여기서 application_는 1씩 증가되면서 붙으니 자신이 돌린 잡의 seq를 확인하여 container의 첫번지 seq log를 보면 executor log가 보인다. 이것으로 모든 로그를 볼 수 있음

enable debugging option on aws EMR

aws web에는 EMR을 띄울때 enable debugging하는 옵션이 있는데 boto3
에서는 해당 옵션이 없어서 찾아보니 아래처럼 custom step 으로 해줘야 한다고..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
client = boto3.client('emr', region_name='us-west-2', verify=False)

response = client.run_job_flow(
Name="launch test cluster",
LogUri=S3_LOG_URI,
ReleaseLabel='emr-5.19.0',
Instances={
'MasterInstanceType': 'm4.xlarge',
'SlaveInstanceType': 'r3.xlarge',
'InstanceCount': 3,
'KeepJobFlowAliveWhenNoSteps': True,
'TerminationProtected': False,
'Ec2SubnetId': EC2_SUBNET,
'Ec2KeyName': EC2_KEY,
},
VisibleToAllUsers=True,
JobFlowRole='EMR_EC2_DefaultRole',
ServiceRole='EMR_DefaultRole',
Applications=[
{
'Name': 'Spark'
},
{
'Name': 'Zeppelin'
}
],
Steps=[
{
'Name': 'Setup Debugging',
'ActionOnFailure': 'TERMINATE_CLUSTER',
'HadoopJarStep': {
'Jar': 's3://us-west-2.elasticmapreduce/libs/script-runner/script-runner.jar',
'Args': ['s3://us-west-2.elasticmapreduce/libs/state-pusher/0.1/fetch']
}
}
]
)

cluster_id = response["JobFlowId"]

print('launching...')

waiter = client.get_waiter('cluster_running')
waiter.wait(ClusterId=cluster_id)

print('launch cluster id: {0}'.format(cluster_id))

Software engineering in Google

software engineering in google논문을 보고 몇가지 인상적인 부분을 요약해 봤다.

sw development

  • 하나의 소스 코드 repo.
    • 하나의 거대한 repo에 모든 프로젝트를 관리하고 있고, 누구나 read 권한을 가지고 있다.
    • Chrome이나 안드로이드는 오픈 소스이므로 별도의 repo에 있다.
    • subtree는 owner만 write권한을 가지고 있고 이들의 리뷰 및 승인을 받아야 반영된다.
    • subtree는 최소한 두명이상의 오너가 등록이 필요하고, 하위폴더는 상위폴더의 오너를 상속.
    • 리뷰를 받아야지만 변경이 메인라인에 반영됨
  • 빌드 시스템
    • Blaze라고 하는 분산 빌드환경을 제공
    • Blaze BUILD 파일을 작성하는데, 이름, 소스 파일들, 필요로 하는 라이브러리들(dependency) 이를 build rules이라 함.
    • 빌드는 몇가지 step으로 이루어짐. 예로 빌드 스텝과 링킹 스텝
    • 빌드는 구글 cloud 인프라위에서 돌기 때문에 매우 빨리 결과가 나옴
    • 빌드 결과는 클라우드에 cache됨.
    • presumit checks. 반영되기 전에 테스트를 돌린다거나 static analysis를 돌리는 등의 결과를 피드백 받음.
  • 코드 리뷰
    • 웹 기반의 코드리뷰 시스템이 있고, 이메일과 연동되어 이메일로도 모든 리뷰 상황이 피드백 된다.
    • 리뷰어를 자동으로 추천해주는 시스템도 있음.
    • 리뷰어가 리뷰는 늦게 하거나 승인을 꺼리는 경우 문제가 개발 속도가 저하되는 문제가 있는데, 리뷰어가 여러명이기 때문에 더 나이스한 사람에게 리뷰를
      요청하는 것으로 이문제를 회피할 수 있음.
    • 코드 리뷰 과정은 메일링 리스트에 자동으로 기록으로 남음.
    • experimental repo는 코드 리뷰없이 반영할 수도 있음. 하지만 이를 권장하지는 않음.
    • 변경의 크기를 300라인이하로 작게 만들것을 권장함.
  • 테스팅
    • unit testing은 기본.
    • code coverage를 소스 코드 탐색기에 보여주기도 함.
    • 프로덕션 환경에 디플로이 하기전에는 로드 테스팅을 기본으로 함.
  • 버그 트래킹
    • Buganizer라고 하는 이슈 트래킹 시스템을 사용.
    • 코드 반영을 할때 관련된 이슈를 레퍼런스하게 되어 있음
  • 프로그래밍 언어
    • 5가지 공인 언어(C++,java, python, go, javascript)를 사용할 것을 독려
    • 각 언어에 대한 style guide가 있음.
    • 다른 언어에 대한 연결은 protocol buffer로 함. 이는 Google RPC 라이브러리와 통함되어 있음
  • 디버깅 & 프로파일링
    • 서버들은 디버깅 라이브러리들을 링킹하고 있는데, 크래쉬 상황이나 OOM 상황에서 signal handler가 스택 덤프를 로그 파일에 적게 되어 있고, core file도 남기게 되어 있음
    • incoming/outgoing RPC packet들에 대한 로그나 타이밍등도 남기게 되어 있음
  • Release engineering
    • 대부분의 프로세스가 자동화 되어 있기에 자주 릴리즈 된다. 주별로, 일별로 진행된다.
    • green 빌드로 확인된 변경을 싱크해서 릴리즈 브랜치로 보내고, 이번 릴리즈에 필요한 특정 변경사항들을 cherry-pick한다. 테스트가 실패하거나 하면 다시 cherry-pick한다. 모든 테스트가 성공하면 바이너리와 데이터들을 같이 패키지 한다.
    • 이렇게 릴리즈 후보가 만들어지면, staging 서버로 보내고 integration test를 한다.
    • 이때 production의 트래픽을 staging서버로 보내서 테스트하는 테크닉을 쓴다. 물론 실제 production으로 보내고 카피하여 스테이징으로 보내는것. 이렇게 하면 미리 production환경을 테스트 해볼수 있어 유용하다.
    • 다음은 canary deploy인데, 트래픽을 몇%만 새 서버로 주고, 나머지는 기존 서버로 보내어 새 서버에 큰 문제가 없는지 본후, 서서히 새 서버로의 트래픽을 늘려주고 100%까지 채운후 기존 서버군을 셧다운 한다.
  • launch approval
    • 사용자가 보게되는 서비스는 launch approval을 받아야 한다. 이는 reliability, legal, privacy, security등의 모든 것이 충족되어야 허가가 난다.
  • 회고(post-mortems)
    • 큰 장애가 나거나 사소한 에러가 나도 관련된 사람은 회고 문서를 작성하게 된다. 문제에 포커스가 있고, 재발방지를 위해 어떻게 해야 하는지에 집중한다. 누구를 비난하기 위한 것이 아니다. 문제는 수치로 디테일하게 작성된다.
  • 잦은 재개발(frequent rewrite)
    • refactoring은 비용이 높다. 하지만 구글의 민첩성과 미래에 도움이 된다.
    • 리팩토링은 불필요한 복잡함을 없애고, 더이상 필요하지 않은 기능들을 없앨 수 있다. 또한 새 개발자들에게 ownership을 전파하는 효과도 있다. 그들의 것이라고 느끼면 사람들의 생산성은 높아진다.

project management

  • OKR(Objectives and Key Results)
    • OKR 년단위 또는 분기 단위로 모든 임직원은 OKR을 작성해야 한다. OKR은 측정 가능한 목표다.
    • 좋은 성적을 낸 팀은 다음에 더 높은 OKR 목표를 입력하게 되고, 못한 팀은 더 적은 목표를 입력하게 된다.
  • 프로젝트 승인
    • 정형화된 프로세스는 없다. 대부분은 bottom-up 방식으로 진행된다.

people management

  • engineering manager
    • 사람을 매니지만 하는 역할이다. 엔지니어도 매니징을 할 수 있지만, 엔지니어링 매니저는 사람 관리만! 한다.
    • tech lead는 중요한 테크니컬 결정을 하고, 매니저는 tech lead를 선택하고, 그들의 팀과 성과를 매니지 한다.
    • 코칭을 하고, 팀원의 커리어 발전을 도와주고, 평가를 하고, 보상을 한다.
    • 채용도 관장한다.
    • 보통은 8-12명까지는 관리하고, 3-30명까지 관리할 수도 있다.
  • Software engineer(SWE)
    • 엔지니어와 매니저는 다른 트랙을 가지고 있다
    • 사람을 매니징하는 것은 승진과 별 상관이 없다.
    • 리더십을 보여주는게 중요한데, 리더쉽은 많은 개발자가 사용하는 임팩트 있는 SW를 개발하는 것이다.
    • 이렇게 함으로서, 테크니컬 스킬은 좋으나 매니징에 서툰 사람에게 좋은 커리어 패스를 제공한다.
  • research scientist
    • 예외적은 리서치를 할 수 잇는 사람을 뽑는다
    • 대개 논문등으로 성과를 측정하지만 하는 일은 SWE와 크게 차이가 나지는 않는다.
    • 대개는 SWE와 같은 팀에서 같이 프로젝트를 하게 된다.
  • Site Reliability Engineer (SRE)
    • 운영에 관련된 엔지니어이다.
  • product manager
    • product을 매니징하는 역할.
    • 엔지니어와 조율하면서, 필요한 피처들을 구체화하고 우선순위를 매겨서 전달하는 역할을 한다.
    • 코드를 쓰지는 않지만, 필요한 코드가 쓰여지게 조율하는 역할이다.
  • program manager/technical program manager
    • product manager와 비슷하지만 product을 관리하지 않고 프로젝트와 운영등을 관리한다.
    • TPM은 비슷하지만 특정 기술이 더 필요한 분야에 필요한 사람이다.

spark application template

spark application template을 만들어 보았다.
의외로 설정해줄것이 좀 있기 때문.
프로젝트는 spark template github project 에서 볼 수 있다.

  1. maven scala project 임. official spark도 maven으로 되어 있기도 하고, 원래 스칼라는 sbt라는 빌드 툴을 쓰는데 아마도 속도에서 뭔가 문제가 있는 모양.
  2. scala version은 2.11.12을 사용했고, spark는 spark v2.3.2 Apache Haddop 2.7 and later 버전을 사용함. scala version이 다르면 바이너리도 다르니 신경 써줘야 함.
  3. 문서는 http://spark.apache.org/docs/2.3.2/ 이것을 보면 됨.
  4. 설정을 파일로 빼기위해 typesafe 를 사용함.
  5. DataFrame, DataSet등을 비교하기 위해서 spark-fast-tests 를 사용함.

invoke

rake 같은 것을 python세계에서 fabric을 사용하고 있었는데, fabric이 invoke로 이름이 바뀌었다. 앞으로 이것으로 사용해 봐야겠다.

설치는 아래처럼 하면 되고

pip install invoke

디렉토리에 tasks.py에 아래와 같은 태스크를 정의하면

1
2
3
4
5
6
7
from invoke import task

@task
def co(c, branch):
c.run("git checkout -b {}".format(branch))
def db(c):
c.sudo('ssh -L 33:<remote_db_url>:3306 dev',password='<sudo_password>')

inv db

을 바로 부를 수 있다. 좋은 것은 상위 디렉토리에 있는 tasks.py를 recursive하게 보기 때문에 위 폴더에 상위 폴더의 task가 inherit된다.

optimistic locking

optimistic locking

https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/DynamoDBMapper.OptimisticLocking.html

dynamo db가 사용하는 optimistic locking
use case가 그럴듯 하여 정리.

dynamo db는 RDB가 아니니 당연 트랜잭션을 사용할수 없음. 그래서 아쉬운 경우가 많음. 이때 optimistic locking을 사용하여 간단히 트랜잭션 처리를 할 수 있어 유용함.
컬럼에 version field를 추가하고 user id(partition key)/version(sort key)로 설정하여 중복된 version이 생성될 수 없게 constraint를 걸어줌.

플로우는 이러함.

  1. row get
  2. 값 변경하고, version +1 증가
  3. 저장. 이때 integrity exception이 발생하면 1부터 다시 시작함.