달력

112017  이전 다음

  •  
  •  
  •  
  • 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
  •  
  •  

Go에 관심을 갖은건 2012년 정도로 생각된다. 하지만!! 지금까지 Go 관련 글을 Evernote나 workflowy에 저장만 하고

딱히 Go를 이용해서 뭘 만들어 보지도 않을 뿐더러 문법도 애매하게 안다...


지금까지 행동 없이 관심만 가지고 지내면서 너무나 무뎌진 나의 개발 능력을 체감했고, 날카롭게 만들고 싶었다.

(물론 무뎌짐을 느낀게 처음은 아니지만 이번엔 세게 왔다. 결심은 큰 의미 없으니 어느정도 생각이 잡히면 바로 행동)

우선 내가 날카롭게 만들고 싶은게 무엇인지 적어보면 

다양한 데이터를 손쉽게 수집하기,

수집한 데이터와 사용 용도에 적합한 DB solution을 찾을 줄 알고, 장/단 점 파악 및 설정 값들이 어떤 의미가 있는 정도, 

마지막으로 저장된 데이터를 쉽게 관리 할수있는 시스템 만들기다. 


우선 Go의 장점/단점이 어떤게 있는지 알아보고, 명분을 찾아보자.




Why go is Not Good  http://yager.io/programming/go.html

Generic, Type overloading 이 없다

immutable data type, pattern matching, option이 없다

대부분 multiple return의 두번째 값을 통해 오류처리를 하는데 잊어버리기 쉽다.

embedded programming에는 overhead가 없는 Rust 가 짱짱맨



Why we switched from Python to Go https://getstream.io/blog/switched-python-go/

C++, Java와 비슷한 성능

직관적인 코드로 생산성 괜춘

goroutine과 channel을 이용한 쉬운 동시성 코딩 (1977년에 시작된 CSP를 새로운 접근 방식으로 구현)


부족한 (CRUD API 지원되는) Framework과 패키지 관리 시스템

부족한 Error Handling


Python 과 Go를 비교

코딩 시간은 Python이 조금 더 짧았지만 최적화 하는데 시간이 오래 걸렸다.

Go는 최적화가 필요하지 않을정도로 괜찮은 성능이 나왔다.



Why Go and Rust are not competitors https://dave.cheney.net/2015/07/02/why-go-and-rust-are-not-competitors

Rust는 C/C++ 호출이 overhead 없이 가능, Go엔 cgo라는게 있지만 성능 이슈가 있다.

Rust는 극강의 성능을 지향, Go는 단순함을 유지 하기 위해 성능을 희생

Go는 단순함이나 직교성을 해치는것을 막는것에 초점을 잡았고

Rust는 unsafe하거나 오버헤드, 즉 성능에 영향을 끼치는것을 막는데 초점이 잡았다.

Rust는 성능을 요구하는 C++, D 같은 언어와 경쟁한다. ex) 게임 엔진, 웹 렌더링 엔진

Go는 인터넷 2.0세대 이후 생긴 Ruby, Python, Node.js 그리고 배포에 오랜 시간이 걸리는 JVM 기반 언어와 경쟁한다.



Why Go? https://dave.cheney.net/2017/03/20/why-go

메모리 관리에 대해서 안전하다.

개발 생산시간이 컴파일 시간보다 비싸진건 오래전부터다. Go는 컴파일 시간이 짧고, 코드에 모호성이 없다.

코드를 읽는것이 작성 하는것보다 우선 순위가 높다는 기조로 만들어졌다. 그리고 다양한 분석 도구들이 생기고 있다.

하드웨어의 발전만으로 성능을 이끌어냈던 공짜 점심은 끝났다. 

goroutine, channel를 이용해 동시성을 지원하는 코드를 손쉽게 작성할수 있다.


How to convince Your company to Go with Golang  https://sendgrid.com/blog/convince-company-go-golang/

Go를 좋아하면 새로운걸 배우는걸 좋아하는 사람들이고 이런 사람들은 우리 회사가 원하는 개발자였다. 채용 개이득!! ㅋㅋ


마음에 드는말

기술로 무언가를 할수 있다는것이 가장 좋은 방법은 아니라는 것을 사람들에게 이해시키는데 도움이 되었던 말 

“Perl로도 계속 개발할 수 있다."

“We can keep doing it in Perl,” which usually helped people to understand that just because you can do something with a technology doesn’t mean its the best way to do it.



Ten Reasons Why I Don’t Like Golang  https://www.teamten.com/lawrence/writings/why-i-dont-like-go.html

public, private을 대소문자로 구분하는것, scope이 어디까지인지 알기 어렵다

error handling이 어렵다

“convention over configuration” 기조가 작은 프로젝트에선 괜찮지만 커다란 프로젝트에선 너의 발목을 물것이다!!

사용하지 않는 variable이나 import가 있으면 경고가 아니라 컴파일이 안된다.

ternary 연산자가 없어서 불편하다.

generic이 없어서 interface{}를 사용하고 있다.

db 혹은 web 과의 통신이 잦은 프로그램에서는 추천하지 않는다.



Why rust? https://pingcap.github.io/blog/2017/09/26/whyrust/

SQL 계층은 Go의 장점을 십분 활용해 구현했지만, 성능이 중요한 저장 계층에선 Go가 단점이 되었다.

저장소로 RockDB를 사용하기로 했는데 cgo의 오버헤드 무시 못함 



Why Go? http://www.mikeperham.com/2014/10/08/why-go/

https://github.com/golang/go/wiki/FromXToGo



지금까지 tutorial 수준으로 해왔던 경험과 위 링크를 읽은 후 Go에 대한 나의 생각


문법이나 생태계 측면으로는 정말 생존에 필요한 도구만 챙기고 바로 캠핑을 떠나는 모험가 같다. 

좀 더 편안하고 쉬운 캠핑을 위해 더 챙겨가면 좋은데 가볍고 빠른 행동(?)을 위함이다.

하지만 단체로 캠핑을 갈 경우 함께 가는 사람들이 모두 모험가형이 아니라면 좀 더 많은 것들을 챙겨가야 할것이다. 

(Go가 큰 프로젝트에 사용할때 불편하다는 말에 어느정도 공감한다. 물론 크다는 기준이 굉장히 주관적이지만...)


사용성이나 성능면으로는 어떠한 분야를 특출나게 잘하진 않지만 다양한 분야를 어느정도 잘하는 사람 같다.

[학교 성적/회사 성과]도 상위권이고 친구와의 관계도 그리 나쁘지 않고, 이성에게 인기도 어느정도 있는 그런 사람.


Go를 나쁘게 보면 어중간하다고 할수 있지만 두루두루 잘 한다고 볼 수도 있다.


정말 극강의 성능을 필요로 한다면 C/C++나 Rust로, 외부 데이터와의 연동이 많다면 Java로 하고 

그외에는 Go!!, Python의 라이브러리가 좋은게 있으면 Python을 이용하자

(현재 데이터 분석 및 관리하는건 Python이 최고!!!)


우선 Go부터 시작하고 어느정도 익숙해지면 Python도 익숙해져보자 (제발~~~)




저작자 표시 비영리
신고
Posted by 오산돌구

평소 데이터 저장소 관련해서 CockroachDB, ArangoDB와 RethinkDB를 관심있게 보고 있었습니다.
CockroachDB는 확장성과 Consistency를 ArangoDB는 다양한 모델 지원(K-V, Doc, Graph), RethinkDB는 real time 을
무기로 본인들의 DB가 좋다고 내세웠다. (순전히 저의 생각이에요.)


어느날 RethinkDB가 shutdown 했다는 트윗을 보게 되었습니다. 기술적으로 꽤 괜찮았다고 생각했는데 
어떤 이유로 문을 닫는지 궁금해서 관련 글을 찾아보게 되었습니다.

부족한 영어지만 찾은 글들을 정리해보려고 합니다.



https://rethinkdb.com/blog/rethinkdb-shutdown/

공식 홈페이지 블로그에 올라온 shutdown 된다는 얘기, 기술력은 꽤 괜찮았지만 적당한 비지니스 모델을 못 찾아서

shutdown 했다는 얘기와 Stripe에 합류해서 오픈소스로서 RethinkDB를 진행한다는 얘기를 하고있다.


Why RethinkDB failed  ***** 별 다섯개!!


※ 원래는 읽어보고 기억에 남는것만 적으려고 했는데 읽다보니 한줄 한줄 정말 저에게 도움 되는 내용들이 많아서
안되는 영어지만 의역을 하였습니다. (뭐 반 이상은 구글 번역기가 해주었지만요...)

생략하기도 하고 제가 틀리게 해석한 부분이 있을수도 있으니 여기서는 느낌만 가졌으면 좋겠습니다.
오역이 있다면 알려주세요.


우리가 RethinkDB shutdown 발표했을 때 저는 여기서 얻은 교훈을 쓰겠다고 약속했었습니다. 

경험들을 정리할 시간이 필요했고 이젠 명확하게 써보려고 합니다.

Hacker News에 RethinkDB shutdown글에 달린 댓글 들을 종합해봤는데 대부분은 실패한 원인이 아니라 증상이었습니다.


나중에 생각해보니 두가지 를 잘못했습니다. 하나는 최악의 시장을 선택한 것이고 다른 하나는 제품 측정의 잘못된 최적화를 한 것입니다. 하나하나 실수는 RethinkDB 가치를 반 토막 냈습니다. 의미 없는 얘기지만 앞에 두가지 중 한가지라도 했다면 우리는 MongoDB 정도 크기의 회사가 됐을 것이고, 두 가지 모두를 잘했다면 RedHat 정도의 회사가 되었을 것입니다.


Terrible Market

신생 기업은 Oracle 기반으로 세워지지 않기 때문에 새로운 인프라 회사로 돈 벌 기회가 있을 거라 생각했고, 
특히
DB 시장은 아주 큰데 이 시장에 맞는 제품을 만들면 우리는 성공한 회사를 만들 거라고 생각했습니다.


불행하게도 우리는 우리가 생각한 시장에 있지 않았고, 우리는 사용자가 생각한 시장에 있었습니다.

RethinkDB 사용자들은 우리를 오픈소스 개발자 도구 회사로 생각하고 있었습니다. 

오픈 소스 시장은 최악의 시장이 소지가 다분합니다. 천 명의 사용자가 RehinkDB 실제 상업적으로 사용하고 있었는데

이들 대부분은 실제 사용한 시간보다 적은 스타벅스 커피 한 잔 값보다 못한 가격을 지불하려고 했습니다. (말하자면 내기 싫었다는 것입니다.)


사용자들이 지원금을 보내지 않을 정도로 제품이 훌륭한것도 아니고, 개발자들이 예산관리를 안 해서도
자본주의가 무너진것도 아니었습니다. 답은 미시 경제학입니다. 
개발자들은 공짜 개발자 도구를 좋아해서 엄청난 수요가 있는데 공급은 그 수요를 훨씬 뛰어 넘습니다.

대체 가능한 도구들이 많다는 것을 뜻하고 그래서 가격은 0으로 수렴한다. 

내용이 어떻게 적용되는지 보기 위해서 $1.6B 가치와 700명의 직원이 있는 MongoDB $1B 가치와 300명의 직원이 있는 Docke 살펴보겠습니다.
회사는 각자의 시장을 완벽하게 지배했습니다. 연봉도 엄청나고 가치도 급성장하고 있습니다. 


굉장히 좋아 보입니다. 개발자 도구 회사가 아닌 시장의 회사들을 보기 전까진….(SalesForce, Palantir 그리고 Box)

그들이 보기에는 MongoDB Docker 굉장히 작아 보일 겁니다. 


회사를 세우는데 이미 파트너십도 존재하고, 유통 인프라, 미친듯이 성장하는 고객들을 보유 했다는것이
지금 시작하는 스타트업에겐 어떤 의미 일까요?


고집스러운 사용자를 모은다는 것을 의미했다. 괜찮은 B2B 시장으로 시작하는 스타트업은 100개의 기회중에 10개의 실제 판매를 한다면개발자 도구 스타트업은 이것의 10배의 기회가 있습니다.
개발자 도구 회사는 우수한 잠재 고객들을 확보 할수 있습니다. - 굉장히 많은 사람들이 당신의 제품을 다운로드 하는것을 의미.
그런데 실제 판매되는건 조롱받을 정도로 낮습니다.


이것은 도미노 효과를 일으켜 피해를 일으켰습니다.
팀의 사기를 떨어뜨렸고 투자 유치하는것과 유능한 인재들을 영입하는것이 어려워졌습니다.

결국 제품 유통에 효과적인 투자를 할 수 없도록 리소스가 제한되었습니다.
스타트업은 살고 죽는것은 한 순간인데, 초기 유통 문제는 대부분 망하는것으로 이어집니다.


Wrong metrics of goodness(잘못된 제품 측정?)

시장이 잘못된 건 알겠는데 다른 개발자 도구 회사들은  나갑니다. RethinkDB 된 걸까요?

시장은 우리가 어떻게 할수 있는 부분은 없지만 제품 결정은 우리가 할수 있었습니다.

우리는 우아하고 강력하고 아름다운 제품을 만들기로 했고 그래서 다음과 같은 측면에 힘쓰기로했습니다.

  • 정확성 : 굉장히 정확하도록 만들었습니다. (그래서 성능이 떨어진다…)
  • 인터페이스 단순화: 구현의 복잡성은 감수 했으므로 사용자가 간단하게 사용하는것에 초점을 맞췄습니다.
  • 일관성: 모든것을 query 만들려고 했습니다. client 드라이버, 설정, 문서 가능한건 모두!!

http://dreamsongs.com/RiseOfWorseIsBetter.html 에서는 3개가 
대부분의 사용자가 제품에게 바라는게 아니라고 말하고 있습니다.


대신 사용자가 원하는 3가지는

  • 원하는게 있을때 그걸 해결할수 있는 제품
  • 빠른 속도: 우리가 제시하는 실 생활의 부하가 아니라 테스트 가능한 범위에 부하 테스트를 견딜만큼 빠르길 원합니다.
    MongoDB는 슬기롭게 해결했지만 우리는 그렇지 못했습니다.
  • 실제 사용기(?): 우리는 괜찮은 DB 시스템을 만들었지만 사용자는 “X 하기 위한 좋은 방법 원했습니다.
    (hapi
    에서 JSON 문서를 저장하는 방법, 로그를 저장하고 분석하는 좋은 방법, 리포트를 만드는 좋은 방법)


3가지를 한건 아닙니다. 하지만....시장에 내놓기 까지 3년이 걸렸습니다.

3 뒤에 시장에 내놓았더니 대부분의 사용자가그래서 RethinkDB MongoDB 다른점이 뭔가요?” 질문을 했습니다.

위에서 나열한 것들의 중요성에 관 설명했지만, 사용자는 신경도 썼습니다.


솔직히 말하면 상처 받았습니다. 엄청 받았어요. 우리는 사용자들이 MongoDB 선택하는지 이해할수 없었습니다.

※ MongoDB 좋은 나열ㅋㅋㅋ


MonoDB 새로운 릴리즈를 출시하면 사용자들은 개선 사항을 보며 축하를 보냈는데, 저 분노를 느꼈습니다

A 수정했다고 했지만, B 성능을 떨어뜨린 거였고, C 기능을 추가했다고 했지만, D 기능을 줄인 것이었습니다.


하지만 시간이 지나자 MongoDB 일반 개발자를 영웅으로 만들었습니다.
저장은 굉장히 빨랐고, 제품 개발 속도도 빠르게 만들어 주었습니다
우리가 원했던 아름다운 제품은 아니었지만, 동작했습니다.


2014 중반이 되자 우리는 MongoDB 경쟁이 되었습니다, 그래서 MongoDB 차별성을 두어 일 했고 굉장히 멋진 방법으로

realtime push 기능을 만들었고 이를 이용해서 이전에는 만들 수 없었던 앱을 개발하기를 바랬습니다

하지만 갑자기 우리가 realtime push를 생각하고 개발하기 훨씬 전부터 realtime push에만 전념했던 

Meteor Firbase 회사가 등장했습니다.

우리는 다시 시장에서 3 뒤처졌고, 우리는 그들과 경쟁할 수 없다는 것을 깨달았습니다.


Cloud 어때?

종종 사람들이 cloud 서비스를 제공하는건 어떠냐는 제안을 했습니다.

우리같은 작은 database 기업이 클라우드를 이용 한다는것은 스트타업의 망하는 패턴과 비슷한 문제가 있습니다.

빌드, 배포, 관리는 매우 어려운데 모든것에 집중을 해야하기 때문입니다.

하지만 우리는 더이상 선택지가 없었고 클라우드를 어떤 형태로 할지 정하기로 했습니다.


datababase 클라우드로 제공하는 방법은 3가지 방법이 있는데, 호스팅 관리, database service형태로 제공, 

아예 플랫폼 형태로 제공 이렇게 있습니다.


호스팅 관리는 이미 AWS가 꽉잡고 있어 쉽지 않습니다. 

Compose.io와 mLab가 RethinkDB보다 두배나 많은 사용자를 지닌 MongoDB를 제공하는것을 고려할때 

호스팅관리는 우리와 맞지 않다고 생각했습니다. 

(※ 제 생각은 MongoDB를 제공하는데도 두 회사 규모가 작은것을 얘기하는것 같습니다.)


DBaaS은 호스팅 관리보다 좀 더 복잡합니다. DB의 깊은 이해 없이도 사용 할수 있도록 추상화된 노드 관리 기능을 

사용자에게 제공 해야합니다. 이것은 사업적으로 굉장히 도전적이었는데, 

1. DynamoDB나 DocumentDB와 같은 거물급들과 경쟁해야 합니다.

2. 대체할 제품이 많이 있는데 사용자가 자신의 데이터를 스타트업에게 맡기는것을 꺼려합니다.
우리는 DBaaS도 제외 했습니다.


마지막 Paas가 우리의 길이라고 생각했는데, 왜냐하면 우리는 굉장한 기술적 우위를 가졌기 때문이었습니다. 

Firebase와 Meteor는 MongoDB위에서 어플리케이션 레벨에서 realtime 로직을 수행했는데, 

이는 대규모 실시간 query 처리량과 성능의 제한을 둡니다.

반대로 우리는 어플리케이션 레벨뿐 아니라 모든 부분을 다뤘기때문에 Firebase와 Meteor는 할수 없는 
특별한 이점을 제공할수 있었습니다.

그래서 우리는 Horizon과 함께 사용자가 Horizon Cloud에서 RethinkDB와 Horizon을 배포하고 

확장이 가능하도록 작업을 시작했습니다. 하지만 작은 팀들이 3개의 대형 프로젝트를 진행하면서 겪는 어려움은 우리를 붙잡았고, 돈이 떨어지기 전에 우리는 cloud로 제공하지 못했습니다. 하지만 엔지니어는 잘 해주었습니다. 거의 완성 단계였습니다.


근본적인 질문

우리는 나쁜 시장을 선택하고, 잘못된 제품 최적화를 했을까요?


어렸을 때 저는 나만의 라디오를 만들고 싶었습니다.
합판으로 박스를 만들고 안에 금속을 집어 넣고 전원선과 박스를 연결했습니다.

그때 집에 전자공학 책이 있었지만 책이 없어도 만들수 있다는 확신이 있었습니다.

마침내 라디오를 만들었습니다. 하지만 전자공학 기초를 배울 필요성을 깨닫는데 몇년이 걸렸습니다.


초기 RethinkDB 사례와 거의 흡사했습니다.
우리는 제품이나 시장에 대한 직관이 없었기 때문에 우리가 해야할지에 대한 정확한 이해 없이 회사를 세웠습니다. 

더군다나 우린 낙관주의자였습니다. 경제 법칙과 회계는 우리와 상관없다고 생각했지만 아니었습니다.


어렸을때 라디오를 만들었을때와 별반 다르지 않습니다. 우리는 사업적으로 서툴렀고, 이것을 깨닫는데 몇년이 걸렸다.


몇몇 사람들은 우리가 go-to-market 팀을 꾸렸다면 지금보다 훨씬 잘했을거라고 얘기합니다다.

초기에 우리는 go-to-marker 전문가의 필요성에 대해 알지 못했고, 그래서 재무팀을 포함해 찾을 생각을 안 했습니다.

우리가 현실을 깨달았을때는 이미 자금이 부족했고, 가뜩이나 어려운 시장에 나가는 경쟁자들,
그리고 우리의 제품이 3 정도 뒤쳐졌다는 것을 깨달았습니다.


세계에서 유능한 go-to-market 팀도 우리를 살릴수는 없었습니다.



맺으며

많은 사람들은 개발자 도구 시장에 대해서 굉장히 강한 인상을 가지고 있습니다. 

엔지니어는 개발자 도구를 만드는것을 굉장히 좋아해서 개발자 도구 회사가 번창하기를 원합니다.


나는 시장을 무시하는걸 망설이고 있습니다. 이유는 3가지인데

한번의 경험으로 이렇게 생각하는것을 바라지 않습니다.

그리고 나는할수 없다라는 말을 싫어합니다.

마지막으로 예외적으로 성공한 회사들이 있습니다. GitHub, MongoDB, Docker같이 나가는 회사도 있고,
GitLab, Unity 같이 해내는 회사 처럼 하는 회사들이 있습니다


만약에 여러분이 개발자 도구 회사를 만들기 시작했다면 신중하세요.  시장은 대체 가능한 제품들로 가득차있습니다.

사용자들은 높은 기대와 낮은 가격을 기대하고 있습니다. 사용자에게 어떤 가치를 제공해줄수 있는지 깊게 생각하세요.

기억하세요 - 세상이 특정 방향으로 흘러가길 바란다고 세상이 그렇게 만들어지지 않는다.


2009 YCombinator 데모데이때 우리는 투자자들에게 RethinkDB 초기 아이디어를 발표했었습니다.
슬라이드에서 기억해야할 3 가지를 얘기하고 발표를 마쳤습니다

“RethinkDB 대해 3가지만 기억해야 한다면”, “이것만 기억하세요라고 말했고, 효과가 있었습니다.

다른것은 기억 했지만, 마지막에 얘기한 3가지는 기억했습니다.


이글에서 기억해야할 3가지 포인트를 남기려고 합니다. 해당 글을 기억해야 한다면 이것만 기억하세요.


시장을 선택하세요. 하지만 특정 사용자를 위해 만드세요

당신이 무엇을 놓치고 있는지 깨닫는것을 배우고, 당신 팀이 얻기위해 지옥처럼 일하세요.

The Economist를 자세히 읽으세요. 이것은 더 빠르게 당신을 좋게 만들것입니다.



Summarizing RethinkDB why we failed  





Rethinkdb joins linux foundation

CNCF가 RethinkDB를 사면서 라이센스가 ASLv2로 변경됐다는 것과 기존 Github이나 웹페이지, SNS계정들은 계속 운영 됨을
얘기하고 있다. RethinkDB가 shutdown 되면서 RethinkDB 직원든이 Stripe에서 일하게 됐고, 그곳에서 RethinkDB
오픈소스 참여를 통해 지속적으로 발전 시킨다고 한다. 곧 2.4를 릴리즈 하는데 2.5에서는 개발자들이 좀 더 다가가기 쉽게 코드를 개선하는것을 최우선으로 하고 Legacy코드와 기술 부채 제거 하면서 성능 향상이 목표라고 얘기 한다.



https://www.joyent.com/blog/the-liberation-of-rethinkdb

함께 만들어가는 입장에서 AGPL의 문제점을 설명하고  CNCF에서 RethinkDB의 지적재산권(?)을 샀다는 얘기
그리고 Apache Public License 2.0. 로 변경했다는 얘기를 전한다.

RethinkDB meeting Note


라이센스에 관한 설명



RehinkDB is dead long live RethinkDB

Social Tables 서비스를 운영하면서 MySQL 적용했을때의 단점을 얘기하고 Rehink로 옮겼을때의 장/단점을 언급한다.

RethinkDB의 단점이 큰 문제가 아니라는 점을 언급하고 오픈소스가 주는 자유의 힘, 그리고 참여하고 싶다는 얘기로 마무리 한다.



RethinkDB vs Postgres

해당 포스트와 거리는 멀지만 RethinkDB를 잘 사용하다가 shutdown과 license로 인해 PostgreSQL로 전환한 얘기가 흥미로워 추가했다. (※ PostgreSQL에 notify, listen이 있는건 처음 알았네요.)


스타트업을 그만두며 깨달은 3 가지 (이 글도 관련있는 글이라 첨부해봅니다. ㅎ)




※ 내 생각

3년 전 부터인가? 기회가 된다면 1인 기술회사 운영을 막연하게 생각"만"하고,
주위 사람들에게 말하고 다녔습니다. (사회 초년생때 만난 송상욱 형님 영향이 크다고 생각되요.ㅎ)
위에서도 말했지만 저도 기술력만 뛰어나면 마케팅이나 투자 없이도 성공할수 있다고 생각했습니다. 

제프딘이나 더그 커팅, 존 카맥 정도는 되야 앞의 문장이 성립하고,
그게 아니라면 기술뿐 아니라 비즈니스도 뒷받침 되어야하는걸 이제야 알게되었습니다. (참 빨리도 알았네요;;)
(각자 생각하는 성공의 관점에 따라 다르겠지만 돈 많이 버는것, 생존 이후의 step)

더군다나 최근에는 굉장히 멋있는 설계와 꽤 괜찮은 기술을 썼다고 성공 하는것도 아니고, 10년 전 기술을 사용하고
아슬아슬한 설계라고 해서 성공 못하는것도 아님을 깨닫고 있습니다. 

제가 생각했던 시장이 그다지 좋지 않다는것도 알게 되었구요. 단순한 지적 호기심과 인기에 따라 움직이지 않고

제가 잘하는건 무엇이고 지금 해야할 것들을 잘하는 것과 어떻게 연관시킬수 있을지 항상 고민하고 실행 해야겠습니다.

값진 경험을 잘 정리해준 RethinkDB 아끼고, 오픈소스 만세~~~~~


ps:
"Done is better than perfect. 수 백번의 이상적인 생각보다 한번의 실행이 변화의 시작이다."
다양한 요구사항을 손쉽게 수정할수 있고 멋진 설계를 하는게 당연히 좋지만
정해진 시간 안에 완성 하는게 최우선되어야합니다.
주어진 시간 안에 요구사항을 충족하는 동시에 나름 괜찮은 걸 만들수 있도록 많은 연습을 해야겠습니다.

X 같은 프로페셔널리즘 (이곳에서 말하는 프로란 허용할 수 있는 품질을 주어진 시간안에 만들수 있는 사람을 말한다.)

저작자 표시 비영리
신고
Posted by 오산돌구

간만에 쉬는 시간을 갖게되어 branch  전략이나 커밋 메시지, 코드 컨벤션에 대해 다른 사람들은 어떻게 했는지 

살펴보고 정리 하려 한다. (그동안 정말 구현하기에 급급...ㅜ,ㅜ)


우선 이런 마음가짐으로 팀원과 맞춰가자

빡빡하게 하나부터 열까지 규칙을 정하는게 아니라, 널널하게 하자. (말이 애매한데, '굵은것만 정하자...' 정도로 생각함) 실력이 있다면 조금 다른 규칙도 충분히 이해가 되기 때문에 그렇다. 조금 달라도 익숙해지도록 노력 하고, 정~~말 익숙하지 않으면 불편하다고 생각되면 같이 얘기해서 수정해 나갔으면 좋겠다. 상대방을 존중하고 각자 발전을 원하는 개발자들이니까!! 존중과 신뢰. 그리고 코드만 얘기해야지 사심이 들어가지 말자.


. 코드 리뷰


토스랩] 코드리뷰, 이렇게 하고 있습니다.

김포프] 올바른 코드리뷰 프로세스

김포프] 코드 몽키를 위한 코딩 스탠다드 

Realm] Codereview HowTo

Github] Using Pull Request


. master branch is always deployable
. Github 의 P/R을 적극 활용. @을 사용하거나 설정을 통해 리뷰어들에게 메일이 가도록 설정하기
. 제품의 품질 향상을 위해 하는거다. 커맨트를 할땐 권유? 형식으로 진행하기
. 기능은 feat/xxxx, 버그 수정은 fix/xxx 브랜치로 만들기
. 큰 기능 혹은 이해가 되지 않은 로직에 대해서는 직접 얘기하는게 좋을것 같다.


. 커밋 메세지


SUBJECT

\n

BODY

http://www.laurencegellert.com/2013/07/how-to-write-a-proper-commit-message/

https://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message

http://chris.beams.io/posts/git-commit/


. JIRA 이슈단위로 움직이므로 HEAD에 이슈번호 붙이기

. SUBJECT는 50자, BODY는 72자 넘지 않도록 하기
    (SUBJECT에 '.'찍지말라는데 이유는 한자한자가 아까운데 그걸 왜 쓰냐 정도,
      50, 72숫자가 나온 이유는 git 만든 Linus가 권고 했다함 -_-;;)

. Github에 JIRA서비스 연동해서 full url을 적지 않아도 이슈 번호로만 JIRA 연동되도록 하기

. 상대방이 커밋 메시지를 읽은 후 코드 봤을때 이해가 쉽게 되도록 해야한다는 배려심



. 코드 규칙


. 현재 nodeJS, Java, Scala를 사용하고 있다. 많은 사람들이 인정한 코드 컨벤션을 따르기로 한다. IntelliJ config로 만드는 작업 필요
. NodeJS는 airbnb, Java는 Google, Scala는 Scala doc, databricks 



이 글을 작성하면서 다시 한번 느낀건 상대방 존중과 배려가 함께 해야한다는 점.

저작자 표시 비영리
신고
Posted by 오산돌구

와~!!! 콘솔쉘 마무으리!!!



키보드, 인터럽트, 타이머에서 원하는 동작을 하기위해 해당 포트에 정의된 데이타를 주고 받는다.
디바이스 드라이버에 대해 자세히는 몰라도 대략적으로 어떻게 코드를 짜야되는지 맛을 봤던 좋은 시간이었다.


읽을때는 이해했는데 시간 지나고 다시 읽으려고하니 기억이 나질 않는다. 양도 많고, 기억력도 썩 좋질 않아서...
4부 가기전에 1, 2, 3부 정리 좀 하고 가야지


하나 아쉬운건 CS, DS와 실제 코드나 데이타가 메모리에 올라가는 관계에 대하여 명확하지가 않다...

써놓고도 뭔말인지...우선 직진이다!! ㅋㅋㅋㅋ

저작자 표시 비영리
신고
Posted by 오산돌구

아~!! 드디어 IA-32e 모드로 넘어갔다.



의지가 약해서 듬성듬성 하다보니까 오래걸렸다. 이젠 놀만큼 놀았다고 생각해서 우직하게 준비해야되는데 동기부여가
아직 덜 됐나보다. ImageMaker때문에 이틀정도 걸렸다... WriteKernelInformation 함수에 lseek부분에서 오류가 발생!
이틀동안 삽질하다가 포기하는 마음으로 #include <unistd.h> 추가해줬더니 정상동작했다!! : )


asm이나 간단한 C소스는 직접 입력했는데 ImageMaker, makefile, elf_xxx.x파일은 복붙했다.   앞으로 뭐가 있나 다시 봤는데...타이핑하는게 더 줄것같다ㅋㅋㅋㅋㅋ



10장에서 IA-32e 모드에 스택을 지정하는 부분이 있는데 Stack segment 를 0x10으로 설정하고 주석은 다음과 같았다.

; 스택을 0x600000~0x6FFFFF 영역에 1MB 크기로 생성

응!!  0x10이랑 0x600000 은 뭔 관계지? 했는데 딱맞는 글이!!  http://jsandroidapp.cafe24.com/xe/4588

즉 16비트 리얼모드만 하한선을 정하고 32/64에서는 의미없다. 0x10은 세그먼터 셀렉터의 개념으로 

세그먼트 디스크립터를 가리킨다.

저작자 표시 비영리
신고
Posted by 오산돌구

2015년 1월 초에 unstable에 merge된 QuickList에 알아본다.

https://matt.sh/redis-quicklist 에서 나온 얘기를 조금 하고 코드를 보자


Redis에 List 자료구조는 일반적인 Linked list와 메모리를 절약을 위한 Ziplist두개가 있다.

포인터를 이용하여 리스트 구현한것과 배열을 이용하여 리스트를 구현한것이라고 생각하면 쉽다.

list-max-ziplist-entries 512
list-max-ziplist-value 64

(노드 갯수가 512개 이상 || 저장하려는 값의 길이가 64 byte 초과)면 Linked list로 저장이 되고

(노드 갯수가 512개 미만 && 저장하려는 값의 길이가 64 byte 이하)면 ziplist로 저장이 된다.

재미진건 ziplist -> Linked list변환은 되는데 Linked list -> ziplist 변환은 안된다.
(하긴 511개 노드였을때 넣다뺐다 하면 변환하다가 끝나겠네...그리고 추가전에만 타입 변환 검사 한다.삭제할때는
검사 안함)


http://dol9.tistory.com/187


Linked list의 장점은 추가/삭제가 쉽다는 점이고, 단점으로는 작은 값(예:4 byte)을 추가하는데 실제 값보다
더 많은 메타데이타?(예: prev, next 포인터, 8 byte)를 사용한다.

Ziplist의 장점은 내부적으로 포인터를 안써서 메모리 절약이 된다는 점이고, 단점으로는 리스트 중간에 노드를
추가/삭제를 하게되면 메모리 재할당, 복사/이동이 일어난다.



2014년 2월 3일에 봤을때 Redis에 리스트 자료구조는 QuickList만 사용한다.

https://github.com/antirez/redis/pull/2143 QuickList에 대해 정말 활발한 리뷰 오고가는걸 볼 수있다.
(재밌겠다~~~~~!!!!)


각 노드를 ziplist로 구성하고 적정 크기로 유지하면서 각각의 노드는 포인터로 연결하는 자료구조 그게 QuickList이다.

[ziplist 0] <-> [ziplist 1] <-> ... <-> [ziplist N]

데이타를 ziplist에 추가하다 조건이 충족되면 노드를 하나 더 생성해서 새로운 노드에 데이타를 저장하거나 노드를
split한후 데이타를 저장하고 Merge하는 작업
을 한다.

재미진건 ziplist에서 더 아끼기위해 압축을 한다.


QuickList에 대해 대략 알아봤으니 궁금한 부분을 나열하고 그에 대해 알아보자


. 데이타 traverse

. 데이타 추가할때 동작

Compression의 동작과 발생 조건

Decompression의 동작과 발생 조건

. 노드 Merge 동작과 발생 조건

. 노드 Split 동작과 발생 조건

로 나눠서 하면 좀 간단하지 않을까 했지만 결국엔 주저리 떠드는걸로 됐음



. 데이타 traverse

pop이나 push명령이 아닌 중간에 있는 노드에 작업을 해야할때가 있다.

기존 ziplist, Linked list는 하나하나씩 노드 곱씹어가며 index 도달할때까지 이동하는데

QuickList는 quicklistNode->count에 ziplist의 데이타 갯수가 저장되있어서 마치 skiplist처럼 탐색을 한다.


. 데이타 추가할때 동작을 보기전에 관련 설정값을 알아보도록 하자

list-max-ziplist-size는 한 노드의 ziplist 최대 크기또는 최대 갯수를 정한다. -5~-1은 최대 크기(byte),
양수는 최대 갯수를 
의미한다. 1로 해놓으면 성능 더 안 좋은 Linked list라고 보면 된다.

list-compress-depth는 head, tail부터 안쪽으로 이동하면서 압축 하지 않을 노드의 깊이를 정한것이다. 
compress에서 다시 보자.

# For a fixed maximum size, use -5 through -1, meaning:

# -5: max size: 64 Kb  <-- not recommended for normal workloads

# -4: max size: 32 Kb  <-- not recommended

# -3: max size: 16 Kb  <-- probably not recommended

# -2: max size: 8 Kb   <-- good

# -1: max size: 4 Kb   <-- good

list-max-ziplist-size -2


# Lists may also be compressed.

# Compress depth is the number of quicklist ziplist nodes from *each* side of

# the list to *exclude* from compression.  The head and tail of the list

# are always uncompressed for fast push/pop operations.  Settings are:

# 0: disable all list compression

# 1: depth 1 means "don't start compressing until after 1 node into the list,

#    going from either the head or tail"

#    So: [head]->node->node->...->node->[tail]

#    [head], [tail] will always be uncompressed; inner nodes will compress.

# 2: [head]->[next]->node->node->...->node->[prev]->[tail]

#    2 here means: don't compress head or head->next or tail->prev or tail,

#    but compress all nodes between them.

# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail]

# etc.

list-compress-depth 0



데이타 추가전에, _quicklistNodeAllowInsert 함수로 해당 노드가 가득찬 상태인지 아닌지 판단한다. 

그리고 추가되는 위치가 ziplist의 처음이면서 정방향인지, ziplist의 마지막이면서 역방향인지도 판단한다. 

총 6개로 구분해서 추가 로직을 구현했는데 간단하게 알아보자


대부분의 분기문에서 해당 노드를 decompress하고 데이타 추가 작업후 compress를 한다. 

얼마나 안타까운 현실인가 LINSERT 명령어는 자제 해야겠다.
비즈니스상 꼭 필요한 거라면 list-compress-depth를 0으로 사용해서 압축을 안하던가...




. 노드 Merge 동작과 발생 조건

. 노드 Split 동작과 발생 조건

발생조건은 데이타 추가에서 알아본 6개의 분기문중 마지막 분기문에서 split과 Merge가 발생한다.



노드 Merge할지 말지 결정할때 사이즈를 구하는데 11을 빼는이유는 ziplist Header사이즈 제거
(두 노드중 한 노드는 header 필요없음)




[A]<->[B]<->[C]<->[D]<->[E]

Merge 는 기준이 되는 노드를 인자로 받아 A/B Merge , D/E Merge , B/C Merge , C/D Merge 가 발생한다.(쉽게 설명한 감이 있음, 사실 앞에서 Merge 가 발생되면 달라짐)


Split 은 동작이 단순 무식하다. split할 노드와 동일한 노드를 새로 만들어서, 

한 노드는 (offset+1)~끝까지 지우고 다른 하나는 0~offset까지 지운다.



. Compression의 동작과 발생 조건

ㄴ 노드 생성하고 기존 노드에 포인터 연결후 기존 노드 compress

ㄴ LSET 명령으로 값 수정 후 해당 노드 compress

ㄴ 노드 merge 할때 count가 큰 노드 compress (ziplist 합치는 작업시 작은 ziplist가 큰 ziplist에 merge된다.)

ㄴ iter 사용후 release할때 iter에서 가리키고 있는 노드 compress

ㄴ A <-> B 노드가 있고 iter가 A의 ziplist 마지막으로 가리키고 있을때 Next 실행 시 A 노드 compress
(iter가 B의 ziplist 처음을 가리키고 있고 Prev 실행시 B 노드 compress)


compress는 두가지 방법으로 한다. 하나는 인자로 들어온 노드만 compress하는 방법이 있고,

다른 하나는 list-compress-depth 변수를 이용하는 방법이 있는데 다음과 같다.


list-compress-depth=2, 인자로 들어온 노드=[E] 

[A]<->[B]<->[C]<->[D]<->[E]<->[F]<->[G]

바깥쪽에서 안쪽으로 list-compress-depth만큼은 compress 안하겠다는 말인데,[A], [B], [F], [G] 노드를 decompress하고 [E]를 compress한다
아...이건 설명하기 참 거시기 하다. compress 방식을 두개로 나눈 이유는 decompress 에서 알아본다.


. decompress의 동작과 발생 조건

ㄴ 노드 compress 작업할때 decompress

ㄴ 두 노드 Merge 할때 두 노드 decompress

ㄹ 데이타 삽입시 노드 decompress

ㄹ 데이타 삭제시 노드 decompress


ㄹ로 되어있는 발생조건이 ㄴ과 다른건 recompress값을 1로 대입하는것 말고는 없다.
차이는 해당 노드만 compress할것이냐, 아니면 compress 두번째 설명한 방식대로 할것이냐 차이다.
ㄹ조건을 보면 데이타 추가/수정/삭제인 경우이다. matt의 섬세함이 느껴졌다.
한번 decompress/compress 한 노드이니까 굳이 할 필요가 없는거지...



따라가기도 버거운걸 코드로 표현한 matt...참 대단하다 난 QuickList 자료구조만 알아본건데
aof, rdb등 전체적인 구조 다 수정 했을텐데...무서운사람....덜덜

antirez가 활발하게 커뮤니케이션 하는거 처음봤다. 부러움이 넘쳤다, 재밌겠다 ㅜ,ㅜ.
영어로 내 의견 쓸 정도는 되야하는데...

기승전 자기반성 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

저작자 표시 비영리
신고
Posted by 오산돌구
TAG redis

BootLoader test

개발하면서 2015.01.25 20:06

책산지는 거의 2년정도 된것같다. 혼자 커널을 공부하자니 막막해서 따라하기 식으로 하면 될것같아
덜컥 사버렸다. 1년전 시도했던것 같은데 환경 세팅하다가 흐지부지 됐던...으악!!! 진득하게 좀 하자!!
실습하면서 생긴 이슈나 깨달은것들을 남기기로 했다 나중을 위한것도 있지만 나 스스로에게 동기부여를 위해서?;;;


이번 포스트는 p.131까지 진행했다.


p.59에 binutils 빌드를 하는데 아래와 같은 오류가 발생했다.

intl이라는 라이브러리를 못찾겠다고 한다. 3년도 더 된 책이라 버젼이 많이 달라졌다.
뜨끔하면서 세월 더 지나가기전에 책 한번 봐야겠다는 의지가 생긴다.
binutils빌드는 책보다 http://jsandroidapp.cafe24.com/xe/3171 보면서 하고, intl라이브러리가 없다고 하는건
아래 그림과 같이 libintl-devel, libintl8 바이너리 설치를 해주면 된다.



qemu는 공식 사이트보다 http://kkamagui.tistory.com/attachment/cfile9.uf@113BF3354EA8233D2B1020.zip 로
하는게 좋은것같다.


부트로더 테스트 성공!



레지스터가 익숙하지 않아서 진행하다가 레지스터 얘기 나오면 3.2장을 펼쳐보게 된다.
짧은 호흡으로 계속 가서 좀 익숙해져야겠다.


====================== 2015/01/29 추가 ======================

@gnutel님 만나서 얻은 꿀팁  virtual box에서 설치한 리눅스 환경에서 64비트 멀티코어 OS원리와 구조 실습하기


1. 우선 Xming을 설치한다. http://sourceforge.net/projects/xming/

2. putty 설정을 수정한다.


3. apt-get, aptitude같은걸로 qemu, nasm 설치

4. windows에서 작업한 Disk.img 복사해서 테스트(이미지는 5장꺼...ㅋㅋㅋ)



작업한걸 옮기자니 그게 귀찮아서 윈도우로 실습 하고있는건 모순....ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ



저작자 표시 비영리
신고
Posted by 오산돌구

요새 좀 보고싶은 오픈 소스들이 죄다 C++로 되어있어서 학습하고있다

http://www.learncpp.com/  여기서 후다닥 보고있는데 집중력이 20분을 못가네....ㅋㅋㅋㅋㅋ;;;


Pointer 와 reference의 차이

레퍼런스가 syntactic sugar인건 알겠는데, 포인터와 차이점은 무엇인가?

  1. 포인터는 재할당이 되지만, 레퍼런스는 한번 초기화 하면 변경하지 못한다.
  2. 포인터는 null을 가리킬수 있지만, 레퍼런스는 꼭!어떤걸 참조 해야한다.
  3. 포인터 자체 주소값은 가져올수 있지만, 레퍼런스는 할수 없다.
  4. reference arithmetics가 없다.
http://yosefk.com/c++fqa/ref.html  (fqa가 갑이구나...)


Constructor initialization lists

composition 혹은 상속관계에 있는것들은 저렇게 초기화하는걸 강력하게 추천!!! 이라고 한다
(const, reference상관없이)


Derived class initialization


생성자를 호출하게 되면 5가지의 초기화가 발생하는데

  1. 메모리 할당
  2. 적합한 생성자 호출
  3. initialization list 실행
  4. 생성자 body 실행
  5. 호출한 곳으로 제어권 넘김
여기서 상속받은 클래스는 한가지가 더 추가된다
  1. 메모리 할당
  2. 적합한 생성자 호출
  3. 적합한 부모 생성자 호출로 부모 객체 생성
  4. initialization list 실행
  5. 생성자 body 실행
  6. 호출한 곳으로 제어권 넘김

자식 클래스에서 부모 클래스 변수를 초기화 하는 방법은?  initialization list로는 할 수 없다. initialization list는 
같은 클래스의 변수만 초기화 할수 있다
const, reference 변수때문. 그리고 제약을 함으로서 모든 변수의 초기화는 한번만 하는것을 보장해준다

부모 클래스의 변수는 다음과 같이 초기화 시킨다.

Inheritance and access specifiers

상속 타입, 멤버 변수 접근자 default는 private이다

멤버 변수 접근자 3개(public, private, protected) * 상속 타입 3개(public, private, protected) 총 9개의 조합이 생성된다.    미안하다 복붙한다...ㅜ,ㅜ

Public inheritance
Base access specifierDerived access specifierDerived class access?Public access?
PublicPublicYesYes
PrivatePrivateNoNo
ProtectedProtectedYesNo
Private inheritance
Base access specifierDerived access specifierDerived class access?Public access?
PublicPrivateYesNo
PrivatePrivateNoNo
ProtectedPrivateYesNo
Protected inheritance
Base access specifierDerived access specifierDerived class access?Public access?
PublicProtectedYesNo
PrivatePrivateNoNo
ProtectedProtectedYesNo


Virtual Base classes

다중 상속의 문제점중에 하나가 diamond problem이다. Base class가 두번 호출이 된다.  그때 virtual 사용
virtual function 사용할때 자식 클래스에는 안써도 문제 없지만, 씀으로서 상기시킨다는 의미로 virtual을 쓰는걸 추천한다.  
virtual function에서 파라미터나 리턴 타입이 다르면 의도한대로 동작하지 않는다.

예외적으로 리턴 타입이 자식,부모 관계의 포인터나 레퍼런스라면 동작한다. (value는 안되네요;;)


(실력 부족이 가장 큰 원인이겠지만 C++은 다른 언어보다 알아야할게 너무 많은것같다. 기본만 하고 프로젝트 보면서
하나하나 공부하는걸로~)

저작자 표시 비영리
신고
Posted by 오산돌구

CLOCKSYNC


1년전에 JM북에 나온대로 무식하게 풀기로 풀었는데 다른 풀이법을 보니 신기했었다

그걸 지금 정리해본다.  분명히 1년전에는 이해했었는데, 다시 이해하는데 꽤나 버벅거림...;;;


10개의 스위치중에 한개의 스위치로만 시침을 변경할수있는 시계가 존재한다

1번 스위치에 11번 시계가 그렇고, 4번 스위치에 8번 시계가 그렇다


1번이나 4번으로 각각의 시계를 12시로 맞춰놓는다

예를들어 4번 스위치로 8번시계를 12시로 맞춰놓았다고 하면 다음할건 4번 스위치를 제외한 9개의 스위치에서

한개의 스위치로만 변경되는 시계가 있는지 살핀다

2번의 10번 시계가 그렇다


icpc IRC에서 Being님이 설명해준걸 이해한 후에 충격과 공포!!





저작자 표시 비영리
신고
Posted by 오산돌구

http://en.wikipedia.org/wiki/Geohash

http://geohash.org/site/tips.html


어느 DB 프로젝트 issue를 보다가 GeoHash dataType 지원할 생각은 없냐는 글을 봤다.

GeoHash? 처음 들어보는 hash다...공부해보자.

공부라고 해봤자 wiki를 내 나름대로 정리하자는 거지 뭐...


GeoHash는 Gustavo Niemeyer가 geohash.org 웹서비스를 개발하면서 위도/경도를 표현하기 위해 
개발한 geocode라고 한다.


좌표 2개를 하나로 표현한다는 장점이 있고, 

가까운 두 점이라도 geohash값은 전혀 다른값이 나온다는 한계가 있다.

대충 비슷한값이 나온다.


latitude 위도, longitude 경도

위도는 적도를 기준으로 북 90, 남, 90이라 하고, 경도는 본초자오선(처음들어봄..)을 기준으로
동경 180, 서경 180라 한다.

Equator 적도, meridian 자오선


우선 어떻게 진행되는건지 알아보자


ezs42 라는 Geohash 값이 있다고 하면 아래 표의 의해서 

01101 11111 11000 00100 00010 로 표현이 된다.



그럼 가장 왼쪽에 있는 0을 기준으로 짝수에 해당하는건 경도, 홀수는 위도이다.

왼쪽 카드가 위도, 오른쪽 카드가 경도 요런느낌? ㅎㅎㅎ


출처:http://theknaveofclovers.tumblr.com/post/41608490725/card-shuffle


그럼 경도는 0111110000000, 위도는 101111001001 가 된다.

10진수로 변환하고 싶지만 여기선 아니다.

binary search Tree처럼 왼쪽에서 오른쪼으로 하나하나씩 위치를 좁혀가는 과정을 진행한다.

위도를 예를 들어보자.


위도는 -90 ~ +90이다. 비트값이 0이면 -90 ~ 0, 1이면 0 ~ +90인것이다.



오른쪽 마지막까지 가면 소숫점 둘째나 셋째에서 반올림해준다.


인코딩은 거꾸로 하면 되겄지?


https://github.com/lyokato/libgeohash

https://github.com/the42/cartconvert

Go, C로 구현한게 있는데...어렵다. 공부하자


참고로 서울 GeoHash는 wydm99uq이다.

http://geohash.org/wydm99uq

저작자 표시 비영리
신고
Posted by 오산돌구

Mac 에서 C/C++ 오픈소스를 좀 보려고 했는데 겪었던 문제들. 새삼 Ubuntu LTS의 소중함을 알았음



1 ArangoDB 컴파일 하는데 자꾸 V8에서 컴파일 에러가 발생했다


unused private field???  아래 설명한 대로 XCode 버젼도 바꿔봤는데 여전히 안되다가 gcc버젼을 4.7로 변경하니

v8 컴파일 성공 :)

https://github.com/cowboyd/libv8/issues/94


sudo port search gcc47, sudo port select --list gcc, sudo port select --set gcc mp-gcc47



2. Eclipse CDT에서 디버깅하면서 소스 좀 보려고 했는데 아래와 같은 메세지 발생


http://stackoverflow.com/questions/19877047/eclipse-gdb-macosx-mavericks

https://sourceware.org/gdb/wiki/BuildingOnDarwin


참고해서 gdb 인증 풀어서 해결. port로 gdb설치하면 ggdb로 실행파일이 생기는건 충격 뭐지...

brew는 /usr/local/Cellar/xxx   port 는 /opt/local/bin/xxx  에 설치가 된다.

인증서 생성방법은
keychain Access.app 실행 후 위쪽 메뉴에서 키체인 접근 -> 인증서 지원 -> 인증서 생성 누른다.
첫 화면에서 이름은 gdb-cert, 신원 유형은 자체 서명 루트, 인증서 유형은 코드서명 마지막으로 기본값 덮어쓰기 click 후
"인증서에 대한 위치 지정"이 나올때까지 계속을 누르고 인증서에 대한 위치 지정은 로그인이 아닌 시스템으로 한다




codesign 등록후 taskgated를 재시작 하라고 하는데 activity Monitor.app 실행 후 taskgated 검색해서 종료하면 재시작 된다.

codesign, keychain access가 있는지도 몰랐고, port, brew 사용법이 어눌해서 삽질했던 시간이었다.

저작자 표시 비영리
신고
Posted by 오산돌구

ArrayList, CopyOnWriteArrayList, LinkedList, Vector, CheckedList,


ArrayList

멤버 변수에는 저장된 데이타의 갯수 size와 데이타를 담는(C에서 포인터처럼 가리키는) Object elementData[]가

정의되어 있다.


넉넉하게 elementData를 잡아놨다가 저장 공간이 부족하면 grow를 호출한다.

또한 elementData 중간에 데이타를 추가하거나, 제거할때는 그 뒷부분의 배열을 앞쪽으로 당기거나 뒤쪽으로 미는
동작을 수행한다.        마지막에 위치한  데이타를 삭제하고 추가시 elementData의 이동은 없다.


여기까지가 기본적인 데이타 추가/수정/삭제 얘기고 ArrayList 보면서 재미진 부분에 대해(지극히 주관적입니다;;) 얘기한다.


* removeAll, retailAll method  사실 이런게 있는지 처음 알았다... A.removeAll(B)는 A에 A-B의 데이타가 남고

A.retailAll(B)는 A와 B의 교집합 데이타가 A에 남는다.


* 데이타를 추가/삭제시 modCount++를 수행하는데 그 이유는 writeObject 에 있다.

writeObject 를 보면 Object를 쓰기전에 modCount를 저장해놨다가 쓰기 작업이 완료된 후, 쓰기 작업 중간에
추가/삭제 작업이 진행됐는지 체크한다.

* sublist가 있는데 사용법은 아래 url에 잘 나와있다.

http://stackoverflow.com/questions/6189704/how-to-take-a-valid-sublist-in-java  
전체가 아닌 부분에 대해서만 작업을 하고 싶을때 사용하면 좋다고 한다.



Vector에서 ArrayList와 가장 큰 차이점이 public method들이 synchronized를 사용한다는 점이다. (생성자와 enumeration 제외) 그래서 writeObject에서는 사용하지 않는다.


Stack은 Vector를 상속하고 5개의 method가 추가되었다. LIFO 동작을 위해 필요한 method들이다.
javadoc에서 더 확실한 LIFO를 사용하고싶다면 Deque를 사용하라고 한다.


LinkedList는 배열이 아닌 포인터(이렇게 말해도 되려나..)를 이용해 양방향 링크드 리스트를 구현하였다.

synchronized가 아니고, 다른 자료구조와는 다르게 modCount를 사용하지 않는다. 그래서 writeObject시 쓰는 중간에

추가/수정/삭제가 실행되도 에러가 발생되지 않는다. 


CopyOnWriteArrayList는 ArrayList를 thread-safe하게 만든 자료구조다.
멤버 변수로 ReentrantLock lock을 갖고있고, 추가/수정/삭제시 lock을 건다. 그리고 clone 시에는 얕은복사를 하고,
추가/수정/삭제시에 데이타를 갖고있는 array 복사를 한다.
추가/수정/삭제시 매번 복사가 발생하므로 데이타 변경이 빈번한곳에서는 쓰면 안될것같다.

저작자 표시 비영리
신고
Posted by 오산돌구
TAG Java

기초가 많이 부족하다는 것을 느껴서 다시 공부려고 하다가 JCF라는걸 알게되었습니다.

자바는 자료구조가 다 구현되어서 마냥 좋다고 쓰기만 했지 이런게 있는지도 몰랐네요.  

꾸준히 할지는...모르겠지만 한번 다 보도록 몸부림 쳐보려고요 : )   

앞으로 글은 인터넷의 자료들을 제 나름대로 정리하는 식으로 진행하려고 합니다.

틀린 부분이 있으면 알려주세요. 혼자 하는거라 이게 맞는건줄 알아요.....ㅜ,ㅜ

================================================


java collection framework를 치면 기본적으로 다음과 같은 그림이 나온다.

 

Map은 Collection과 연관이 없는데 그 이유에  대해서 이렇게 설명하고 있다.(http://howtodoinjava.com/2013/07/09/useful-java-collection-interview-questions/#why_map_not_extend_collection)

Map은 Collection과는 다른 구조이기 때문이다. Collection에는 add(Object o) 함수가 있는데 Map은 key-value 구조

이므로 다르다. 그리고 Map은 keySet, valueSet, entrySet함수가 있는데 Collection에는 지원하지 않는다.


List, Queue가 어떻게 구현되었는지 알아본다.


마지막으로 http://howtodoinjava.com/2013/07/09/useful-java-collection-interview-questions/

소개한 질문들을 소스를 보면서 파악해본다.


Map은 삼실청년 블로그에 잘 설명되어있어서 패스하고 Set은 Map을 멤버 변수로 가지고 동작하는 자료구조라 패스!!


ref : http://howtodoinjava.com/2013/07/09/useful-java-collection-interview-questions/#what_is_collection_in_java

http://www.cse.ohio-state.edu/software/2231/web-sw2/extras/slides/31.Java-Collections-Framework.pdf

저작자 표시 비영리
신고
Posted by 오산돌구
TAG Java

이 기능은 2013년 1월달에 unstable로 merge되었습니다.

https://github.com/antirez/redis/commit/4cdbce341ebff64d392a42011f4a9258f8aa834f#src


처음 시작할때는 2.6으로 했는데 하;;; 시간을 너무 끌었네요. 뭐든지 한번할때 왕창해야겠어요.

간단하게 말하면, 이 기능의 요지는 key가 추가/수정/삭제가 되었을때 이벤트를 잡아서

client한테 알려주는것입니다


슬기로운 antirez는 redis에 있는 기능을 이용해서  간단하게 구현을 했습니다.

이 글을 보시기전에 Pub/Sub을 보시고 오시면 더 좋겠습니다.


key에 대해 이벤트(추가/수정/삭제)가 발생하면, 어떤키인지, 그리고 어떤 이벤트인지 두가지로 나뉩니다.
체널명을

__keyspace@[dbid]__:[key name] [event name], __keyevent@[dbid]__:[event name] [key name] 로 생성합니다.


또 재미있는건 모든 자료구조(+ expire, evicted, generic)마다 flag를 두었다는 점입니다.
redis.conf를 보면 아래 flag가 있습니다

#  K     Keyspace events, published with __keyspace@<db>__ prefix.
#  E     Keyevent events, published with __keyevent@<db>__ prefix.
#  g     Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...
#  $     String commands
#  l     List commands
#  s     Set commands
#  h     Hash commands
#  z     Sorted set commands
#  x     Expired events (events generated every time a key expires)
#  e     Evicted events (events generated when a key is evicted for maxmemory)
#  A     Alias for g$lshzxe, so that the "AKE" string means all the events.


redis.h

/* Keyspace changes notification classes. Every class is associated with a
 * character for configuration purposes. */
#define REDIS_NOTIFY_KEYSPACE (1<<0)    /* K */
#define REDIS_NOTIFY_KEYEVENT (1<<1)    /* E */
#define REDIS_NOTIFY_GENERIC (1<<2)     /* g */
#define REDIS_NOTIFY_STRING (1<<3)      /* $ */
#define REDIS_NOTIFY_LIST (1<<4)        /* l */
#define REDIS_NOTIFY_SET (1<<5)         /* s */
#define REDIS_NOTIFY_HASH (1<<6)        /* h */
#define REDIS_NOTIFY_ZSET (1<<7)        /* z */
#define REDIS_NOTIFY_EXPIRED (1<<8)     /* x */
#define REDIS_NOTIFY_EVICTED (1<<9)     /* e */
#define REDIS_NOTIFY_ALL (REDIS_NOTIFY_GENERIC | REDIS_NOTIFY_STRING | REDIS_NOTIFY_LIST |

REDIS_NOTIFY_SET | REDIS_NOTIFY_HASH | REDIS_NOTIFY_ZSET |

REDIS_NOTIFY_EXPIRED | REDIS_NOTIFY_EVICTED)      /* A */


K, E는 위에서 말한 두가지이고, 'g$lshzxeA' 문자열을 가지고 골라먹는 재미가있습니다.

[K | E | KE][g, $, L, s, h, z, x, e | A]


하나 의문점이 드는게 있습니다. g의 expire와 x의 expire는 왜 두개나 있지? hdel을 하면 g에 걸리는거여,
h에 걸리는거여?  등 중복될것같은 생각이 들었습니다.


코드를 보면, expire명령어를 실행하면 g, expire가 되서 key가 지워지면 x,

hash에 item이 지워지면 h, db의 key자체가 지워지면 g입니다.

그리고 redis.io에서 얘기한게, 있지도 않은 키에대한 수정/삭제는 이벤트가 발생하지 않습니다.


key가 추가/수정/삭제시 위의 flag랑 이벤트명, key명을 가지고 호출하는 함수는 notifyKeyspaceEvent 입니다.



자 이젠 이 알람을 subscribe하면 됩니다
redis.io 문서에서는 아래와 같이 하라고 합니다.

$ redis-cli config set notify-keyspace-events KEA
$ redis-cli --csv psubscribe '__key*__:*'
Reading messages... (press Ctrl-C to quit)
"psubscribe","__key*__:*",1


성능은.....아무래도 느려지긴 할것 같습니다. 특히 __key*__:* 가......덜덜

저작자 표시 비영리
신고
Posted by 오산돌구
TAG redis

JM북과 함께 PS의 재미를 알게되고 삼주전? 부터인가 topcoder의 재미를 알게되었습니다.

지금까지는 그냥 단순한 알고리즘 이해 수준에 머물렀다면 앞으로는 PS를 많이 경험할 생각입니다.


topcoder를 재미를 알게된 3주전부터 C++를 하고 다른사람의 C++소스도 보고 있는데

C랑 비슷하면서도 많이 틀린...그런 오묘한 기분이네요.


C++공부하면서 STL도 써볼겸  NQUEEN 문제를 풀어봤습니다.

뭐 돌아가긴 가는데..........실행시간이........안습...ㅠ,ㅠ.


결국은 pan[MAX_N][MAX_N]으로해서 걸리나 안걸리나 check하면서 풀었다. ; ) 아 재미져
제출하고 다른 사람소스 보니....흠....미친듯이 공부해야겠구나


저작자 표시 비영리
신고
Posted by 오산돌구

팀내 과장님을 통해 루씬 4.1이 나온 걸 알았습니다.

이번 기회에 미루고 미루던 루씬이 내부적으로 어떻게 구성되는지 보려고 (아는 게 없으니, 무작정 해 보자라는 마음으로) 

이클립스에 import 하려고 했습니다.

http://softwaregeeks.org/2012/10/30/how-to-import-lucene40-in-eclipse/  링크처럼 진행 했는데,
제가 뭘 잘못 건드렸는지는 모르겠지만 아래와 같은 오류가 발생 했습니다.  

(ant ivy-bootstrap resolve 까지 진행한 상태, ant 설정은 위 url 참고.)




이 글을 위의 화면 처럼 나오는 분들께 바칩니다. ㅎㅎ


우선  New -> Java Project 에서 lucene이라고 프로젝트를 하나 만듭니다.

그 다음 lucene 폴더에서 우 클릭을 누르고 import -> File System,   과감히 lucene-4.1.0을  선택합니다.





그럼 왼쪽 그림 처럼 프로젝트가 구성됩니다.



우선 lucene core를 보는게 목적 이었기때문에  

core -> src -> java 에서  Build Path -> Use as Source Folder

core -> src -> test 에서  Use as Source Folder



테스트에서 빨강색이 왕창 떠서 가봤더니

test-framework, codec이 없다고 난리




test-framework -> src -> java Use as Source Folder

codecs-> src -> java       Use as Source Folder


그리고 test-framework -> lib에 있는 library들을 빌드에 추가 시킵니다.

ant-1.8.2.jar, junit-4.10.jar, junit4-ant-2.0.8.jar, randomizedtesting-runner-2.0.8.jar

Build Path -> Add to Build Path




여기까지 했으면 빨강색은 뜨지 않습니다.

TestIndexWriter.java로 가서 테스트 유닛을 돌려봅니다.    vm Argument 에 -ea 인자 주라고 합니다.

가볍게 추가해주고 다시 실행~!!


에러보기


멘붕입니다.

더 멘붕인건 ant test로 하면되는데, eclipse에서 테스트하면 안되는게.....역시 얕은 지식으로는 한계가 금방 나타납니다.


이것 때문에 루씬 재설치를 수차례, 혹시나해서 library 받은거 지우기도 했는데 안됩니다.......;;

trace에 찍힌 소스들 들어가서 보지만, 뭔소린지.......


이것저것 해보다가 포기상태있을 때, 뭔 에런가.....하고 한줄한줄 읽어봤습니다.


"Caused by: java.lang.IllegalArgumentException: A SPI class of type org.apache.lucene.codecs.Codec with name 'Lucene41' does not exist. You need to add the corresponding JAR file supporting this SPI to your classpath.The current classpath supports the following names: []"


You need to add the corresponding JAR..You need to add the corresponding JAR..You need to add the corresponding JAR......You need to add the corresponding JAR........You need to add the corresponding JAR


혹시나 해서 lucene/core, lucene/codecs, lucene/test-framework에서

build를 해서(폴더 이동후 ant만 실행해주면 만들어줘요) jar 파일을 만든 후에 추가했습니다.  (jar는 lucene/build에 폴더별)




다시 TestIndexWriter.java Junit 궈궈 ~~~~~~~~~~~~~~  


잘됩니다. : )


도움이 되셨으면 좋겠습니다.

더불어 루씬 소스 보신 분들의 조언, 함께 하시고 싶은 분들 알려주세요~~

제가 백지부터 시작 하는 거라 함께 백지에 글 써 가듯이.....: )


※교훈 : 에러 로그를 잘 보자. 영어를 공부하자. 응? ㅋㅋㅋ

저작자 표시 비영리
신고
Posted by 오산돌구

Redis의 aof에서  fork하는 부분이 있습니다.


구글한테 물어보니 다음과 같은 모드가 있었네요.

set follow-fork-mode mode
Set the debugger response to a program call of fork or vfork. A call to fork or vfork creates a new process. The mode can be:
parent
The original process is debugged after a fork. The child process runs unimpeded. This is the default.
child
The new process is debugged after a fork. The parent process runs unimpeded.
ask
The debugger will ask for one of the above choices.
show follow-fork-mode
Display the current debugger response to a fork or vfork call.


동시에 두개를 볼 수 는 없나봅니다.(child / parent) 생각해보니 두개 동시에 보면 디버깅 하는 입장에서 더 괴롭겠어요 ㅋㅋ

혹시 동시에 두개 하는 방법이 있나요?


부지런히 코드 타이핑해서 생각한걸 코드로 뚝딱뚝딱 만들 수 있는 날이 왔으면 좋겠습니다 : )



GDB모드 원문 url : http://sunsite.ualberta.ca/Documentation/Gnu/gdb-5.0/html_node/gdb_25.html

저작자 표시 비영리
신고
Posted by 오산돌구

Slowlog는 지정한 시간이 넘어서는 command를 기록해서 나중에 관리차원에서 볼수있게 해줍니다.


설정으로 slowlog-log-slower-than, slowlog-max-len을 설정합니다.

slowlog-log-slower-than는 microseconds단위로써,
지정한 시간보다 느린 command를 기록합니다. threshold같은거죠.음수로 지정하면 slowlog를 사용하지 않고,
0으로 하면 모든 command를 기록합니다.

slowlog는 list 자료구조에 저장하는데, 이 List의 최대 크기를 지정하는 설정이 slowlog-max-len 입니다.
(검색하면서 알았는데, MySQL Slow Query log 란게 있었네요.

이젠 소스 분석하는만큼 직접 만들어보는것도 시간을 쏟아야겠어요 /엉엉/)



slowlog.c는 slowlogEntry를 만드는 slowlogCreateEntry, 해제하는 slowlogFreeentry,

server start할때 server.slowlog 초기화해주는 slowlogInit,

그리고 실제 slowlog-log-slower-than 이상 걸리는 명령어에 대해 로그를 저장하는 slowlogPushEntryIfNeeded

"slowlog reset" 를 담당하는 slowlogReset, 마지막으로 저장되어있는 slowlog의 정보를 볼수 있는

slowlogCommand 함수가 있습니다.


알아볼건, slowlogPushEntryIfNeeded입니다. 그리고 마지막으로 실제 해보고 slowlog를 마치겠스니다.

slowlogCommand도 후보였는데, 'slowlogEntry, length를 보고, slowlog를 reset하는 기능이다' 이게 끝이라;;;


slowlogPushIfNeeded 함수는 먼저 server.slowlog_log_slower_than이 음수면 return입니다.

그리고 command의 duration이 설정한 값보다 크면 그때 slowlogEntry를 만들어서

server.slowlog에 add해줍니다. 

argc, argv가 있는데 어간 redisClient 명령어 argument입니다. 또 인자의 갯수가 너무 많을수도 있는걸

고려해서 최대 32개의 인자만 저장하도록 되어있습니다.


마지막으로 server.slowlog-max-len이 server.slowlog의 max_length먄큼의 크기를 유지하는 작업을 합니다.



각 명령어마다 총 4개의 설명이 나옵니다.

1)은 server.slowlog_entry_id로서 slowlog_entry별 고유값입니다.

2)는 명령어가 실행한 unix timestamp

3)은 명령어가 실행하는데 걸린 시간, 단위는 microseconds입니다.

4)는 실제 명령어가 저장됩니다.



http://garantiadata.com/blog/enhancing-redis-slow-log#.URRHvdFx50w

그리고 오른쪽 "Don't let Redis scalability limitations turn into business limitations." 

동영상이 있는데 참 재미있게 만들었습니다.

물론 속도가 빠르고, 다양한 기능이 있는것도 중요하지만, 관리하는것도 그만큼 중요하다는 것을 다시한번 느낍니다.

저작자 표시 비영리
신고
Posted by 오산돌구
TAG redis

Redis 2.6부터 Bit operation이 지원되었습니다.


BITOP [NOT | AND | OR | XOR ], BITCOUNT, SETBIT, GETBIT  입니다.


Bit operation에 쓰이는 자료형은 String입니다.  String이 512MB가 제한입니다.

그래서 bit offset 의 최대 길이는 0 ~ (512*1024*1024*8 -1 )입니다.


SETBIT, GETBIT, BITCOUNT, BITOP 순으로 진행하겠습니다.


우선 SETBIT는 "SETBIT key offset bitvalue(0 | 1)" 입니다.

offset이 위에서 말한 bit 범위인지, bitvalue는 맞게 들어왔는지 검사합니다.


그 후에 해당 key가 기존 DB 존재 여부를 판단하고, 없으면 String형을 생성하고

있으면 value의  Type 검사와 참조 하는 데가 있는 지를 판단해서 처리를 해줍니다.


(offset >> 3) + 1 만큼 메모리  공간을 확보한 다음에 offset >> 3에 해당하는 데이타를 가져온 후

해당 offset에 bitvalue를 set 해줍니다.


======================= Example ===========================

key user1에는 00000000 01000000 00000000이 저장되어있고
setbit user1 20 1 실행한다고 하면

(10 >> 3) + 1  크기의 메모리를 할당한후, (키가 존재하고, 이미 더 큰 메모리가 할당 되있으면 이 부분은 패스~!!)

bit = 7 - (bitoffset & 0x7); 의 동작을 하는 이유는 한 byte는 0~7의 오프셋이 있고,

사람이 이해하기 편하게 하려고 한것같습니다.(제 추측....;;)


( (uint8_t*)user1에 해당하는 value->ptr)[(10 >> 3)] 을 가져오고 또한 offset의 값을 가져옵니다. 

offset의 값을 가져오는 이유는 client에게 원래 있던 값을 리턴하기위함입니다.
그리고 가져온 바이트는 3번째 byte 00000000 을 가져오겠네요.


이젠 ( (uint8_t*)user1에 해당하는 value->ptr)[(10 >> 3)] 값에 원하는 값을 세팅할 차례입니다.

byteval &= ~(1 << bit)
offset의 비트는 0으로 나머지 비트는 그대로 둡니다.. ( offset의 값만 set해주기위해)

byteval |= ((on&0x1) << bit);
그 다음 3번째 바이트와 ((on & 0x1) << bit); 결과와 OR연산을 합니다.

( (uint8_t*)o->ptr)[byte] = byteval;  로 set을 마칩니다.

======================= Example ===========================



Bit Operation에 대해 알아보겠습니다. 단순합니다. XOR, AND, OR, NOT bit 연산을 하는데,
그 방식이 4~xx 번째 인자의 value들을 bit 연산한 결과를 3번째 인자에 저장을합니다.


4~xx 의 인자들의 value를 가리킬 포인터와(unsigned char **src),
각 value의 길이(long *len)를 저장할 변수에 값을 대입합니다.   대입하면서 maxlen과 minlen을 찾습니다.


4~xx의 인자의 갯수가 16개 이하면, 이중포문으로 각 인자들의 value들을 minlen만큼 bit 연산을 합니다.

왜 16개인지는 모르겠네요;;   이중포문이 끝나면, minlen~maxlen에 대해서 bit 연산을 합니다.

4~xx의 인자의 갯수가 16초과면 바로 아래 포문을 탑니다. 

위의 이중포문에서는 unsigned long * 4 씩 뭉탱이로하고, bit연산자별로 나뉜다음에 포문이 있는데,

아래는 포문안에 switch가있고, byte로 연산을 합니다.


위 for문에서 연산한 결과를 3번째 인자에 저장합니다. 이미 해당 key가 있다면 지우고 다시 저장합니다.



마지막으로 bitcount가 있습니다. start와 end차이(byte단위)를 구해서 해당하는 byte의 bit가 1인걸 count합니다.

이 부분에 재밌는 두가지 있습니다. (나만 그럴라나......ㅋㅋㅋ)

첫번째는 16byte씩 뭉탱뭉탱 count하는것...  CountBitsSetParallel  에 있는걸 사용했습니다.

그냥보면 그저 멍합니다. 실행해보는게 답이죠 ㅋㅋ


결과 (보기편하려고 띄어쓰기는 제가.....;; 머리가 나쁘니 ㅋㅋㅋ)
0000 1100 1100 1101 0101 1011 1010 0100
0000 1000 1000 1001 0101 0110 0101 0100
0000 0010 0010 0011 0010 0011 0010 0001
0000 0000 0000 0000 0000 0000 0000 1111
Bit Num : 15
계속하려면 아무 키나 누르십시오 . . .
와우~!! 계산기랑 수작업으로 했는데 맞게 나오네요. 당연한걸.....ㅋㅋㅋ
대충은 알겠는데 마지막은 왜 저런지;;;; 알려주세요.

두번째는 위에서 뭉탱뭉탱하고 나머지 byte들은 1byte씩 count를 하는데,
이걸 또 그냥하지 않고 256배열에 해당 index의 1의 갯수를 미리 입력해 놓았습니다.
static const unsigned char bitsinbyte[256] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,
2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,
3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,
4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8};
ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 아 센스쟁이....ㅋㅋㅋ


bit 연산할때 minlen, maxlen을 잘 따져서 그래도 좀 빠르게 할수도 있지않을까 생각해봅니다....


저작자 표시 비영리
신고
Posted by 오산돌구
TAG redis

Redis의 보안관련해서 소개하겠습니다.

http://redis.io/topics/security


위의 문서중에서 rename-command, masterauth/require가 어떻게 동작하는지 알아보겠습니다.


첫째로, rename-command에 대해 알아보겠습니다.

우선 어떤 기능이냐면, 일반적인 client가 특정 명령어를 못하도록 막는 기능을 합니다.

SORT나 FLUSHALL, CONFIG같은 명령어는 일반 client가 실행하게 하면 안되겠죠.


사용법은 간단합니다. redis.conf에   rename-command [org command] [new command]

ex ) rename-command FLUSHALL kakaka

아래는 실행 화면입니다.



populateCommandTable 함수로 redisCommandTable 에 명령어를 dict에 저장합니다.

그 다음에 loadServerConfig 함수가 호출됩니다.

redis.conf에 있는 설정들을 읽는 부분입니다. 수많은 if-else문이 있지요...ㅋㅋ

rename-command을 보면 아래와 같이 소스가 구성되어 있습니다.


populateCommandTable 에서 server.commands에 load한 command의 정보중 key값을 교체합니다.

sort, randomxxx, flush, config같은 명령어 들은 서버 관리자만 실행 할 수 있도록 바꿔놓으면 좋을것 같네요 ; )



두번째로 masterauth/requirepass에 대해 알아보겠습니다.

두 설정 모두 암호를 걸어 인증을 요구하도록 합니다. 차이점이라면 masterauth는 replication

          (master, slave관계에 있을때 사용)    requirepass는 client에 접속에 이용됩니다.

masterauth는 인증하는게 흠....replication 학습할때 알아보고 requirepass는 어떻게 인증하는지 알아보겠습니다.


서버가 기동될때 redis.conf에 설정한 requirepass는 server.requirepass에 저장이 됩니다.

그리고 client에서 auth 명령어로 입력한 암호와 server에 설정되어 있는 암호와 비교를 하는데, time_independent_strcmp로 합니다.

strncmp로 하면 되지 왜 굳이 모든 char를 비교하는지 모르겠네요;; 혹시 아시는 분있으면 알려주세요.: )

저작자 표시 비영리
신고
Posted by 오산돌구
TAG redis