대용량 서비스플랫폼 인데, 여러 개의 Table을 포함한 처리를 한 Session에서 길게 잡은 것이 가장 큰 실수였다.

대용량 서비스에서는 DB 사용시에는 Transaction을 과감하게 빼야 할 듯.

 

<서비스 확인>

java의 mysql driver에서 아래와 같이 출력.

 

com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction

 

 

<Mysql DB>

 

mysql> SHOW ENGINE INNODB STATUS\G

------------------------
LATEST DETECTED DEADLOCK
------------------------

1. Transaction

1. wating the lock

2. Transaction

2. holds the lock

<해결>
1. DB Session을 아주 짧게 하는 처리로 작업. 
2. Where절의 순서를 교체 (insert intention waiting 방지)
=> 더 이상의 deadlock은 발생하지 않음.
참고 자료

http://dev.mysql.com/doc/refman/5.0/en/innodb-monitors.html

Real MySQL  - 이성욱

Posted by '김용환'
,

 

http://www.mybatis.org/core/ja/xref-test/org/apache/ibatis/executor/BaseExecutorTest.html

 

spring 없이 mybatis 만 이용해서 객체를 만들어서 사용할 경우 참조할 만한 소스

 

View Javadoc

1   /*
2    *    Copyright 2009-2011 The MyBatis Team
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.executor;
17  
18  import domain.blog.Author;
19  import domain.blog.Blog;
20  import domain.blog.Post;
21  import domain.blog.Section;
22  import org.apache.ibatis.BaseDataTest;
23  import org.apache.ibatis.logging.jdbc.ConnectionLogger;
24  import org.apache.ibatis.mapping.MappedStatement;
25  import org.apache.ibatis.session.Configuration;
26  import org.apache.ibatis.session.RowBounds;
27  import org.apache.ibatis.transaction.Transaction;
28  import org.apache.ibatis.transaction.jdbc.JdbcTransaction;
29  import static org.junit.Assert.*;
30  import org.junit.Test;
31  
32  import javax.sql.DataSource;
33  import java.sql.Connection;
34  import java.util.HashMap;
35  import java.util.List;
36  import java.util.Map;
37  
38  public class BaseExecutorTest extends BaseDataTest {
39    protected final Configuration config;
40  
41    public BaseExecutorTest() {
42      config = new Configuration();
43      config.setLazyLoadingEnabled(true);
44      config.setUseGeneratedKeys(false);
45      config.setMultipleResultSetsEnabled(true);
46      config.setUseColumnLabel(true);
47      config.setDefaultStatementTimeout(5000);
48    }
49  
50    @Test
51    public void shouldInsertNewAuthorWithBeforeAutoKey() throws Exception {
52      DataSource ds = createBlogDataSource();
53      Connection connection = ds.getConnection();
54      Executor executor = createExecutor(new JdbcTransaction(connection));
55      try {
56        Author author = new Author(-1, "someone", "******", "someone@apache.org", null, Section.NEWS);
57        MappedStatement insertStatement = ExecutorTestHelper.prepareInsertAuthorMappedStatementWithBeforeAutoKey(config);
58        MappedStatement selectStatement = ExecutorTestHelper.prepareSelectOneAuthorMappedStatement(config);
59        int rows = executor.update(insertStatement, author);
60        assertTrue(rows > 0 || rows == BatchExecutor.BATCH_UPDATE_RETURN_VALUE);
61        if (rows == BatchExecutor.BATCH_UPDATE_RETURN_VALUE) {
62          executor.flushStatements();
63        }
64        assertEquals(123456, author.getId());
65        if (author.getId() != BatchExecutor.BATCH_UPDATE_RETURN_VALUE) {
66          List<Author> authors = executor.query(selectStatement, author.getId(), RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
67          executor.rollback(true);
68          assertEquals(1, authors.size());
69          assertEquals(author.toString(), authors.get(0).toString());
70          assertTrue(author.getId() >= 10000);
71        }
72      } finally {
73        executor.rollback(true);
74        executor.close(false);
75      }
76    }
77  
78    @Test
79    public void shouldInsertNewAuthor() throws Exception {
80      DataSource ds = createBlogDataSource();
81      Connection connection = ds.getConnection();
82      Executor executor = createExecutor(new JdbcTransaction(connection));
83      try {
84        Author author = new Author(99, "someone", "******", "someone@apache.org", null, Section.NEWS);
85        MappedStatement insertStatement = ExecutorTestHelper.prepareInsertAuthorMappedStatement(config);
86        MappedStatement selectStatement = ExecutorTestHelper.prepareSelectOneAuthorMappedStatement(config);
87        int rows = executor.update(insertStatement, author);
88        List<Author> authors = executor.query(selectStatement, 99, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
89        executor.flushStatements();
90        executor.rollback(true);
91        assertEquals(1, authors.size());
92        assertEquals(author.toString(), authors.get(0).toString());
93        assertTrue(1 == rows || BatchExecutor.BATCH_UPDATE_RETURN_VALUE == rows);
94      } finally {
95        executor.rollback(true);
96        executor.close(false);
97      }
98    }
99  
100   @Test
101   public void shouldSelectAllAuthorsAutoMapped() throws Exception {
102     DataSource ds = createBlogDataSource();
103     Connection connection = ds.getConnection();
104     Executor executor = createExecutor(new JdbcTransaction(connection));
105     try {
106       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectAllAuthorsAutoMappedStatement(config);
107       List<Author> authors = executor.query(selectStatement, null, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
108       assertEquals(2, authors.size());
109       Author author = authors.get(0);
110       // id,username, password, email, bio, favourite_section
111       // (101,'jim','********','jim@ibatis.apache.org','','NEWS');
112       assertEquals(101, author.getId());
113       assertEquals("jim", author.getUsername());
114       assertEquals("jim@ibatis.apache.org", author.getEmail());
115       assertEquals("", author.getBio());
116       assertEquals(Section.NEWS, author.getFavouriteSection());
117     } finally {
118       executor.rollback(true);
119       executor.close(false);
120     }
121   }
122 
123   @Test
124   public void shouldInsertNewAuthorWithAutoKey() throws Exception {
125     DataSource ds = createBlogDataSource();
126     Connection connection = ds.getConnection();
127     Executor executor = createExecutor(new JdbcTransaction(connection));
128     try {
129       Author author = new Author(-1, "someone", "******", "someone@apache.org", null, Section.NEWS);
130       MappedStatement insertStatement = ExecutorTestHelper.prepareInsertAuthorMappedStatementWithAutoKey(config);
131       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectOneAuthorMappedStatement(config);
132       int rows = executor.update(insertStatement, author);
133       assertTrue(rows > 0 || rows == BatchExecutor.BATCH_UPDATE_RETURN_VALUE);
134       if (rows == BatchExecutor.BATCH_UPDATE_RETURN_VALUE) {
135         executor.flushStatements();
136       }
137       assertTrue(-1 != author.getId());
138       if (author.getId() != BatchExecutor.BATCH_UPDATE_RETURN_VALUE) {
139         List<Author> authors = executor.query(selectStatement, author.getId(), RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
140         executor.rollback(true);
141         assertEquals(1, authors.size());
142         assertEquals(author.toString(), authors.get(0).toString());
143         assertTrue(author.getId() >= 10000);
144       }
145     } finally {
146       executor.rollback(true);
147       executor.close(false);
148     }
149   }
150 
151   @Test
152   public void shouldInsertNewAuthorByProc() throws Exception {
153     DataSource ds = createBlogDataSource();
154     Connection connection = ds.getConnection();
155     Executor executor = createExecutor(new JdbcTransaction(connection));
156     try {
157       Author author = new Author(97, "someone", "******", "someone@apache.org", null, null);
158       MappedStatement insertStatement = ExecutorTestHelper.prepareInsertAuthorProc(config);
159       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectOneAuthorMappedStatement(config);
160       int rows = executor.update(insertStatement, author);
161       List<Author> authors = executor.query(selectStatement, 97, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
162       executor.flushStatements();
163       executor.rollback(true);
164       assertEquals(1, authors.size());
165       assertEquals(author.toString(), authors.get(0).toString());
166     } finally {
167       executor.rollback(true);
168       executor.close(false);
169     }
170   }
171 
172   @Test
173   public void shouldInsertNewAuthorUsingSimpleNonPreparedStatements() throws Exception {
174     DataSource ds = createBlogDataSource();
175     Connection connection = ds.getConnection();
176     Executor executor = createExecutor(new JdbcTransaction(connection));
177     try {
178       Author author = new Author(99, "someone", "******", "someone@apache.org", null, null);
179       MappedStatement insertStatement = ExecutorTestHelper.createInsertAuthorWithIDof99MappedStatement(config);
180       MappedStatement selectStatement = ExecutorTestHelper.createSelectAuthorWithIDof99MappedStatement(config);
181       int rows = executor.update(insertStatement, null);
182       List<Author> authors = executor.query(selectStatement, 99, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
183       executor.flushStatements();
184       executor.rollback(true);
185       assertEquals(1, authors.size());
186       assertEquals(author.toString(), authors.get(0).toString());
187       assertTrue(1 == rows || BatchExecutor.BATCH_UPDATE_RETURN_VALUE == rows);
188     } finally {
189       executor.rollback(true);
190       executor.close(false);
191     }
192   }
193 
194   @Test
195   public void shouldUpdateAuthor() throws Exception {
196     DataSource ds = createBlogDataSource();
197     Connection connection = ds.getConnection();
198     Executor executor = createExecutor(new JdbcTransaction(connection));
199     try {
200       Author author = new Author(101, "someone", "******", "someone@apache.org", null, Section.NEWS);
201       MappedStatement updateStatement = ExecutorTestHelper.prepareUpdateAuthorMappedStatement(config);
202       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectOneAuthorMappedStatement(config);
203       int rows = executor.update(updateStatement, author);
204       List<Author> authors = executor.query(selectStatement, 101, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
205       executor.flushStatements();
206       executor.rollback(true);
207       assertEquals(1, authors.size());
208       assertEquals(author.toString(), authors.get(0).toString());
209       assertTrue(1 == rows || BatchExecutor.BATCH_UPDATE_RETURN_VALUE == rows);
210     } finally {
211       executor.rollback(true);
212       executor.close(false);
213     }
214   }
215 
216   @Test
217   public void shouldDeleteAuthor() throws Exception {
218     DataSource ds = createBlogDataSource();
219     Connection connection = ds.getConnection();
220     Executor executor = createExecutor(new JdbcTransaction(connection));
221     try {
222       Author author = new Author(101, null, null, null, null, null);
223       MappedStatement deleteStatement = ExecutorTestHelper.prepareDeleteAuthorMappedStatement(config);
224       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectOneAuthorMappedStatement(config);
225       int rows = executor.update(deleteStatement, author);
226       List<Author> authors = executor.query(selectStatement, 101, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
227       executor.flushStatements();
228       executor.rollback(true);
229       assertEquals(0, authors.size());
230       assertTrue(1 == rows || BatchExecutor.BATCH_UPDATE_RETURN_VALUE == rows);
231     } finally {
232       executor.rollback(true);
233       executor.close(false);
234     }
235   }
236 
237   @Test
238   public void shouldSelectDiscriminatedProduct() throws Exception {
239     DataSource ds = createJPetstoreDataSource();
240     Connection connection = ds.getConnection();
241     Executor executor = createExecutor(new JdbcTransaction(connection));
242     try {
243       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectDiscriminatedProduct(config);
244       List<Map> products = executor.query(selectStatement, null, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
245       connection.rollback();
246       assertEquals(16, products.size());
247       for (Map m : products) {
248         if ("REPTILES".equals(m.get("category"))) {
249           assertNull(m.get("name"));
250         } else {
251           assertNotNull(m.get("name"));
252         }
253       }
254     } finally {
255       executor.rollback(true);
256       executor.close(false);
257     }
258   }
259 
260   @Test
261   public void shouldSelect10DiscriminatedProducts() throws Exception {
262     DataSource ds = createJPetstoreDataSource();
263     Connection connection = ds.getConnection();
264     Executor executor = createExecutor(new JdbcTransaction(connection));
265     try {
266       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectDiscriminatedProduct(config);
267       List<Map> products = executor.query(selectStatement, null, new RowBounds(4, 10), Executor.NO_RESULT_HANDLER);
268       connection.rollback();
269       assertEquals(10, products.size());
270       for (Map m : products) {
271         if ("REPTILES".equals(m.get("category"))) {
272           assertNull(m.get("name"));
273         } else {
274           assertNotNull(m.get("name"));
275         }
276       }
277     } finally {
278       executor.rollback(true);
279       executor.close(false);
280     }
281 
282   }
283 
284   @Test
285   public void shouldSelectTwoSetsOfAuthorsViaProc() throws Exception {
286     DataSource ds = createBlogDataSource();
287     Connection connection = ds.getConnection();
288     connection.setAutoCommit(false);
289     Executor executor = createExecutor(new JdbcTransaction(connection));
290     try {
291       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectTwoSetsOfAuthorsProc(config);
292       List<List> authorSets = executor.query(selectStatement, new HashMap() {
293         {
294           put("id1", 101);
295           put("id2", 102);
296         }
297       }, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
298       connection.rollback();
299       assertEquals(2, authorSets.size());
300       for (List authors : authorSets) {
301         assertEquals(2, authors.size());
302         for (Object author : authors) {
303           assertTrue(author instanceof Author);
304         }
305       }
306     } finally {
307       executor.rollback(true);
308       executor.close(false);
309     }
310   }
311 
312   @Test 
313   public void shouldSelectAuthorViaOutParams() throws Exception {
314     DataSource ds = createBlogDataSource();
315     Connection connection = ds.getConnection();
316     connection.setAutoCommit(false);
317     Executor executor = createExecutor(new JdbcTransaction(connection));
318     try {
319       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectAuthorViaOutParams(config);
320       Author author = new Author(102, null, null, null, null, null);
321       executor.query(selectStatement, author, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
322       connection.rollback();
323 
324       assertEquals("sally", author.getUsername());
325       assertEquals("********", author.getPassword());
326       assertEquals("sally@ibatis.apache.org", author.getEmail());
327       assertEquals(null, author.getBio());
328     } catch (ExecutorException e) {
329       if (executor instanceof CachingExecutor) {
330         // TODO see issue #464. Fail is OK.
331         assertTrue(e.getMessage().contains("OUT params is not supported"));
332       } else {
333         throw e;
334       }
335     } finally {
336       executor.rollback(true);
337       executor.close(false);
338     }
339   }
340 
341   @Test
342   public void shouldFetchPostsForBlog() throws Exception {
343     DataSource ds = createBlogDataSource();
344     Connection connection = ds.getConnection();
345 //    connection = ConnectionLogger.newInstance(connection);
346     Executor executor = createExecutor(new JdbcTransaction(connection));
347     try {
348       MappedStatement selectBlog = ExecutorTestHelper.prepareComplexSelectBlogMappedStatement(config);
349       MappedStatement selectPosts = ExecutorTestHelper.prepareSelectPostsForBlogMappedStatement(config);
350       config.addMappedStatement(selectBlog);
351       config.addMappedStatement(selectPosts);
352       List<Post> posts = executor.query(selectPosts, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
353       executor.flushStatements();
354       assertEquals(2, posts.size());
355       assertNotNull(posts.get(1).getBlog());
356       assertEquals(1, posts.get(1).getBlog().getId());
357       executor.rollback(true);
358     } finally {
359       executor.rollback(true);
360       executor.close(false);
361     }
362   }
363 
364   @Test
365   public void shouldFetchOneOrphanedPostWithNoBlog() throws Exception {
366     DataSource ds = createBlogDataSource();
367     Connection connection = ds.getConnection();
368     Executor executor = createExecutor(new JdbcTransaction(connection));
369     try {
370       MappedStatement selectBlog = ExecutorTestHelper.prepareComplexSelectBlogMappedStatement(config);
371       MappedStatement selectPost = ExecutorTestHelper.prepareSelectPostMappedStatement(config);
372       config.addMappedStatement(selectBlog);
373       config.addMappedStatement(selectPost);
374       List<Post> posts = executor.query(selectPost, 5, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
375       executor.flushStatements();
376       executor.rollback(true);
377       assertEquals(1, posts.size());
378       Post post = posts.get(0);
379       assertNull(post.getBlog());
380     } finally {
381       executor.rollback(true);
382       executor.close(false);
383     }
384   }
385 
386   @Test
387   public void shouldFetchPostWithBlogWithCompositeKey() throws Exception {
388     DataSource ds = createBlogDataSource();
389     Connection connection = ds.getConnection();
390     Executor executor = createExecutor(new JdbcTransaction(connection));
391     try {
392       MappedStatement selectBlog = ExecutorTestHelper.prepareSelectBlogByIdAndAuthor(config);
393       MappedStatement selectPost = ExecutorTestHelper.prepareSelectPostWithBlogByAuthorMappedStatement(config);
394       config.addMappedStatement(selectBlog);
395       config.addMappedStatement(selectPost);
396       List<Post> posts = executor.query(selectPost, 2, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
397       executor.flushStatements();
398       assertEquals(1, posts.size());
399       Post post = posts.get(0);
400       assertNotNull(post.getBlog());
401       assertEquals(101, post.getBlog().getAuthor().getId());
402       executor.rollback(true);
403     } finally {
404       executor.rollback(true);
405       executor.close(false);
406     }
407   }
408 
409 
410   @Test
411   public void shouldFetchComplexBlogs() throws Exception {
412     DataSource ds = createBlogDataSource();
413     Connection connection = ds.getConnection();
414     Executor executor = createExecutor(new JdbcTransaction(connection));
415     try {
416       MappedStatement selectBlog = ExecutorTestHelper.prepareComplexSelectBlogMappedStatement(config);
417       MappedStatement selectPosts = ExecutorTestHelper.prepareSelectPostsForBlogMappedStatement(config);
418       config.addMappedStatement(selectBlog);
419       config.addMappedStatement(selectPosts);
420       config.setLazyLoadingEnabled(true);
421       List<Blog> blogs = executor.query(selectBlog, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
422       executor.flushStatements();
423       assertEquals(1, blogs.size());
424       assertNotNull(blogs.get(0).getPosts());
425       assertEquals(2, blogs.get(0).getPosts().size());
426       assertEquals(1, blogs.get(0).getPosts().get(1).getBlog().getPosts().get(1).getBlog().getId());
427       executor.rollback(true);
428     } finally {
429       config.setLazyLoadingEnabled(true);
430       executor.rollback(true);
431       executor.close(false);
432     }
433   }
434 
435   @Test
436   public void shouldMapConstructorResults() throws Exception {
437     DataSource ds = createBlogDataSource();
438     Connection connection = ds.getConnection();
439     Executor executor = createExecutor(new JdbcTransaction(connection));
440     try {
441       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectOneAuthorMappedStatementWithConstructorResults(config);
442       List<Author> authors = executor.query(selectStatement, 102, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
443       executor.flushStatements();
444       executor.rollback(true);
445       assertEquals(1, authors.size());
446 
447       Author author = authors.get(0);
448       assertEquals(102, author.getId());
449     } finally {
450       executor.rollback(true);
451       executor.close(false);
452     }
453   }
454 
455   protected Executor createExecutor(Transaction transaction) {
456     return new SimpleExecutor(config,transaction);
457   }
458 
459 }

This page was automatically generated by Maven

Posted by '김용환'
,

 

git  써보니.. cvs, svn보다 좋은 장점이 있다.

 

1. 자기만의 저장소에서 commit이 가능 (local commit)

 

2. merging이 참 편리

 

3. 다양한 응용이 가능(git flow 같은 녀석이 태생할 수 있는 배경)

 

 

git flow 장점

 

1. 배포 프로세스에 맞는 branch 관리

  hotfix, release, tag

 

2. 배포 프로세스에 맞는 merging이 편리

svn에서는 diff / merging 하다가 시간이 많이 흘러감

 

 

 

svn을 배포프로세스에 맞게 이것저것 작업하는 것보다는 git flow 하나 배워서 적용하는 게 훨 나았던 것 같음.

Posted by '김용환'
,

 

 

Mybatis의 SqlSessionFactory에서 openSession() 메소드를 아무 생각 없이 사용했다. (변 : Spring에 길들여지다보니. Mybatis 의 Transaction에 대해서 무뎌졌다.. )

 

 

/*     */   public SqlSession openSession() {
/*  45 */     return openSessionFromDataSource(this.configuration.getDefaultExecutorType(), null, false);
/*     */   }

 

Executor는 default로, autocommit는 false로 인식한다. 헉..  Transaction이 길어졌다.

 

 

openSession() 메소드를 사용할 때는 꼼꼼히 ExecutorType, TransactionIsolationLevel, autoCommit을 고민하고 사용할 것!.

 

 

 

 

아래 내용 출처 : http://www.scribd.com/doc/68885206/55/SqlSessionFactory

 

image

 

image

Posted by '김용환'
,

convertion pattern에서 %F:%L 사용시 ?:? 로 발생할 수 있다.

 

<param name="locationInfo" value="true"/> 를 추가해야 한다.

 

<appender name="queue" class="org.apache.log4j.AsyncAppender">
    <param name="bufferSize" value="500"/>
   <param name="locationInfo" value="true"/>
    <appender-ref ref="general" />
</appender>

Posted by '김용환'
,

 

3년, 4년 전에 신입개발자들을 데리고 IDC에 다녀온 적이 있다.

 

IDC의 현장에서 일하는 IDC 직원 분들도 보고, 어떻게 일하는지도 보고, 셋팅은 어떻게 했는지 볼 수 있었다.

또한 수천 대의 서버를 어떻게 배선하는지. 회사에서 쓰고 있는 L7 네트웍 스위치들과 DB 장비들, 서버 장비들을 보여주었다. 인프라 담당자와 함께 신입개발자들을 데리고 나오니. 마치 유치원 선생님이 어디 견학 다녀온 것 같았다. PPT나 툴로 만나는 DB나 서버 Component가 아닌 “실재”를 만날 수 있었다. 신입개발자들은 다들 놀라면서 새로운 세상을 알 것처럼 좋아했다.

 

IDC 에서 12시간 교대로 근무했던 운영자들이 있었기에 지금 멋진 세상을 살고 있다는 느낌을 받았다. 그들의 수고에 대해서 감사하게 생각하고 있다..

 

 

 

 

전 직장 A 회사에서 나는 개발자로 입사했지만, 사람 시키기 어려우면 UTP 케이블, UTF Cross 케이블, 케이블 선 사서 endpoint 만들고 배선도 하라고 시켰다. 한 때는 5평 쯤 되는 서버실의 랙 앞에서 하루 종일 서서 Mpeg/Muxer, Emulator, Solaris, Dell 서버들과 함께 살았다. 환기도 잘 안 되는 서버실에서는 탁한 공기를 마시며 허리 통증을 느끼며 일했던 순간들이 주마등처럼 스쳐 지나갔다. 군대에서 행보관이 제초작업을 하기 위해서 작업자를 뽑을 때 해당 부서에서 가장 일이 적다고 느껴지는 사람이 차출 나가 작업하는 것처럼 내가 꼽혔던 것 같다.

 

시간이 지나면서 많이 능숙해졌다.

하드웨어 문제나 물리 전송 단 문제인지, 소프트웨어 문제인지 조금씩 눈이 보이기 시작했다.

 

저 서버의 속은 어떻게 구성되어 있을까? 남는 서버들을 Rack에서 빼서 하나씩 분해해서 cpu, memory, hdd 위치도 파악하기도 했다. 좀 더 특이한 서버들이 올 때마다 껍데기를 분해해서 어떻게 구성되어있는지 확인해봤다.

 

새로운 랙이 왔다며 랙을 옮기라고 했다. 개발하다 뛰쳐나가 랙을 이동하는데.. 죽을 뻔했다. 1 년동안 깡냉이 먹을만한 무게였다. 랙을 옮기면서 무게 바닥에 대해서 고민하기 시작했다. 일반 사무실에서 랙을 한쪽으로 모는 것을 최대한 방지하고 적당히 옮길 수 있도록 해야겠다는 생각을 하게 되었다.

 

이번에는 전원문제가 발생했다. 한쪽으로 랙을 쏠리면 특정 서버군에서 전력이 떨어져 전력을 확보해야 하는 이슈가 생겼다. 멀티탭을 사서 여러 전원 소스로부터 전원을 뽑고 전력을 겨우 겨우 확보했다.

 

그러다 보며 배선 문제가 심각했다. 직원들이 길을 가다 넘어져서 서버의 전원이 나가거나 네트웍이 끊기는 일이 빈번하게 되었다. 사무실 바닥을 다 꺼내서 UTF 케이블, 방송 케이블, 전원 케이블을 한번에 묶어서 각 파트별로 나눠서 쉽게 연결할 수 있게 했다.

 

이런 것들을 해보니, 3년 전이긴 하지만 IDC의 기술이 얼마나 튼튼한지 새삼스럽게 느꼈다.

 

초기 IDC 는 얼마나 힘들었을까? 시행착오를 똑같이 겪지 않았을까? 사무실에서도 이렇게 힘들었었는데…

 

생각해보니 아래 문제들을 가장 고민했었던 거 같다.

(책을 전혀 보지 않고 쓰고 경험적으로 쓴 거라 틀릴 수 있다.)

 

1. 전력 문제 : 언제나 동일한 전류, 전압이 서버로 흘러가야 했다. Capacity 확보는 언제나 중요한 문제였다.

2. 배선 문제 : 배선을 바닥 또는 천정으로 잘 따야 한다. 전원이나 케이블등이 빠져 나가서 발에 걸려 문제를 일으키지 않도록 해야 한다. 결국은 바닥보다는 천정방식으로 가는 게 더 좋은 모델이었던 거 같다.

3. 안정성 : 랙의 길이, 무게를 잘 디자인해야 했다. 무턱 대고 샀다가 빌딩 무너질 수 있다. 층마다 적절하게 무게를 배분해야 하고 해당 층에서 버틸 수 있는 총용량을 넘지 않도록 해야 할 것 같다. 또한 비상 전원은 늘 돌아가게 해야 한다. DB 장비 (Sun) 는 일반 랙과 달라서 로봇 같았다. 조심스럽게 다뤄야 했다.

4. 모니터링 : 모든 장비가 디지털 장비가 아니다. 아날로그 신호를 디지털로 바꾸는 장비도 있고, UTP 케이블만 꽂아 하는 장비도 있다. 다양한 장비를 한번에 모니터링할 필요가 있다. 전원 공급량부터 해서 네트웍까지.

5. 서버 배치 : IP 별로 서버 배치를 잘 하는 게 중요한 거 같다. 이 장비들이 묶을 수 있는 장비가 따로 있어서, 항상 배치를 신경써 야 했다.

6. 서버 이름 : 리눅스의 hostname과 같다. 물리 장비 배치 순서에 맞게 정할지, 그냥 서비스 이름에 맞게 구성할지가 관건이었다. 서비스 이름에 맞게 구성하면 기억하기 훨씬 편했다. 따라서 케이블 선에 네임 태그만 넣으면 알아서 끼고 테스트할 수 있었다.

 

 

지저분한 바닥에서 했던 모든 것들이 어제 일처럼 생생하다. 코딩 하면서 희열을 느낀 순간보다 힘든 시간이 더 기억나기 마련인 것 같다. 그런 순간들을 아무 말 없이 성실하게 일을 진행했다. 그 때는 어쩌면 개발에 대한 스트레스가 커서 그런 일을 즐겼는지도 모르겠다.

 

시스템 담당을 직접 하지는 않았지만, 주어진 일을 하다보니.. 세상을 보는 눈도 약간 넓어지는 것 같기도 하다.

Posted by '김용환'
,

 

Maven 빌드는 성공했는데, Eclipse 상에서 Build 실패가 나는 경우가 있다. api가 없다는 이상한 것이 뜰 수 있다. 분명 소스 보기 하면 해당 lib가 나온다.

다양한 원인이 있을 수 있겠지만. 이번에 만난 케이스가 있어서 정리한다.

 

예를 들어, XML , WEB 관련 패키지가 java 1.6 으로 포함되었는데. 관련 패키지가 jdk 보다 우선 순위가 높도록 해야 한다.

 

project 의 propertie 설정으로 들어가서 java build path 로 접근한다.

 

image

 

jdk library를 xml보다 더 order가 빠르게 둔다.

powermock도 컴파일 이슈가 있어서 아래와 같이 가장 order의 bottolm으로 이동하니. 이클립스 컴파일 상태가 문제가 발견되지 않았다.

 

image

Posted by '김용환'
,

 

git flow release 1.1.0을 마치고 finish 한다.

 

 

$ git flow release finish 1.1.0

...
Summary of actions:
- Latest objects have been fetched from 'origin'
- Release branch has been merged into 'master'
- The release was tagged 'v1.1.0'
- Release branch has been back-merged into 'develop'
- Release branch 'release-1.1.0' has been deleted

 

 

master, develop 머징되고, 두 개의 새로운 태그도 생성된다.

image

 

 

일반적으로 git flow xxx finish 를 하면, local에 있는 branch를 삭제하고, R날짜 태그를 생성한다.

따라서, 로컬에 있는 branch를 삭제하지 않으려면 -k 을 파라미터로 추가해야 한다. 의미는 keep 이다.

 


$ git flow release finish -k 1.1.1


Summary of actions:
- Latest objects have been fetched from 'origin'
- Hotfix branch has been merged into 'master'
- The hotfix was tagged 'v1.1.1'
- Hotfix branch has been back-merged into 'develop'
- Hotfix branch 'hotfix-1.1.1' is still available

 

이를 사용해서 좋은 장점은 필요 없는 'R날짜’ 태그는 더 이상 생성되지 않는다.  태그가 깔끔해지는 효과가 있다.

 

image

Posted by '김용환'
,




1. comic4portable 0.8.5 다운로드 


http://fsd.me/index.php?mid=computer&category=1550&page=1&document_srl=1320

http://nneco.tistory.com/63



2. 사용법

http://evenkiss.blog.me/80163325736

Posted by '김용환'
,

 

1.

일반적인 한글은 UTF-8 인코딩시 3bytes 다. UTF-16 인코딩시는 2bytes..

일본어도 마찬가지이나.. 특이한 한자는 4bytes이다..

 

2.

Horizontal Ellipsis (‘...’) 는 ASCII로도 3bytes이고, UTF-8 인코딩시 3bytes 짜리이다. UTF-16으로 가야 2bytes

 

3.

UTF8인지 확인하는 방법 (java)

  1. String의 값을 ISO-8859-1 방식으로 bytes[] 값을 얻어옴

  2. http://ko.wikipedia.org/wiki/UTF-8 UTF 표현의 특징 값을 찾아서 확인.

     image

 

4.

IOS4 Emoji(이모티콘) 와 IS5 Emoji(이모티콘)의 인코딩 값이 다르다.

 

예를 들어 아래 링크의 Emoji는 IOS4용이다

http://arashnorouzi.wordpress.com/2011/08/31/adding-graphics-and-emoji-characters-to-apple-push-notifications/

Posted by '김용환'
,