2016ǯ09��20��

ISUCON6ͽ����ȥå��̲ᤷ�ޤ���

¤Ï¤Æ¤Ê¥Ö¥Ã¥¯¥Þ¡¼¥¯¤ËÅÐÏ¿

@methane �Ǥ����֤��ε������ˤ����꤬���롪�פȤ���������̾�� @kizkoh (����ե�ô��), @mecha_g3 (���ץ�ô��) �ȤȤ�� ISUCON 6 �˻��路��ͽ����ȥåץ��������̲ᤷ�ޤ����� ����Τդ꤫���국����񤭤ޤ���

�դ꤫����

��ǰ�ʤ��饹�����ϵ�Ͽ���Ƥʤ��ΤǤ������������ˤ�ä����Ȥ�ޤȤ�ޤ��� ���ץ�Υ����ɤ� methane/isu6q-app �Ǹ������Ƥ���Τǡ���̣���������ϥ����ɤ��ǧ���Ƥ���������

strings.Replacer ��Ȥ�

���Ѹ���Ϻǽ餫�� Go �ȷ��Ƥ����ΤǤ�����Go�ν���������٤����ƥ����ॢ���ȤǺǽ餫�饹����̵���Ǥ����� top �ǥ��ץ��CPU������Ū�ʤΤϤ���Ƚ��ޤ������������ɤ��ɤ�Фʤˤ��٤��Τ����ȯ��Ƚ��ޤ���������ʤ�Ĺ���ʤ��ΤǴؿ����Τ�ĥ��ޤ���

func htmlify(w http.ResponseWriter, r *http.Request, content string) string {
	if content == "" {
		return ""
	}
	rows, err := db.Query(`
		SELECT * FROM entry ORDER BY CHARACTER_LENGTH(keyword) DESC
	`)
	panicIf(err)
	entries := make([]*Entry, 0, 500)
	for rows.Next() {
		e := Entry{}
		err := rows.Scan(&e.ID, &e.AuthorID, &e.Keyword, &e.Description, &e.UpdatedAt, &e.CreatedAt)
		panicIf(err)
		entries = append(entries, &e)
	}
	rows.Close()

	keywords := make([]string, 0, 500)
	for _, entry := range entries {
		keywords = append(keywords, regexp.QuoteMeta(entry.Keyword))
	}
	re := regexp.MustCompile("("+strings.Join(keywords, "|")+")")
	kw2sha := make(map[string]string)
	content = re.ReplaceAllStringFunc(content, func(kw string) string {
		kw2sha[kw] = "isuda_" + fmt.Sprintf("%x", sha1.Sum([]byte(kw)))
		return kw2sha[kw]
	})
	content = html.EscapeString(content)
	for kw, hash := range kw2sha {
		u, err := r.URL.Parse(baseUrl.String()+"/keyword/" + pathURIEscape(kw))
		panicIf(err)
		link := fmt.Sprintf("<a href=\"%s\">%s</a>", u, html.EscapeString(kw))
		content = strings.Replace(content, hash, link, -1)
	}
	return strings.Replace(content, "\n", "<br />\n", -1)
}

���������ϤϤƤʥ�����ɡܤϤƤʥ������Τ褦�ʥ����ӥ��ǡ����δؿ��ϥ�����ɤξҲ𵭻����Ф���¾�Υ�����ɤؤΥ�󥯤��������Ĥ�HTML���������פ����ΤǤ���������ɤ�ʸ������ǥ����Ȥ��Ƥ���Τǡ���Ĺ���פǥ�󥯤ˤʤ�ޤ���

������ɤ����󥯤ؤ��Ѵ�����ȯ�Ǥ���Ƥ��ʤ��Τϡ�����Ѵ������HTML���������פ� <a> �����ޤǥ��������פ���Ƥ��ޤ������դ����HTML���������פ򤹤�ȥ�����ɤ򸫤Ĥ����ʤ��ʤ뤫��Ǥ����ʰ��٤���˵��Ť����쵤���Ѵ�����褦�ˤ��ƥϥޤ�ޤ�����

����ɽ���Υӥ�ɤ��ִ��Τɤ��餬�ɤ줯�餤�γ��ǽŤ��Τ��ޤǤϤޤ��ץ��ե������Ϥ�Ƥʤ��ä��ΤǤ狼��ޤ��󤬡���꤫��Go�餷����Ŭ����Ϥ�Ƥߤޤ���

�ޤ�Go������ɽ�����٤��Τ� strings.Replacer ��Ȥäƥ�����ɤ����󥯤ؤ��Ѵ��򤷤ޤ��� Replacer ���ۤ���Τˤ���֤�������Τǡ� htmlify() �Ȥ����ؿ�������ۤ���ΤǤϤʤ�����ư���ȡ�������ɤ��ɲú�����˺ƹ��ۤ򤹤�褦�ˤ��ޤ����ʼºݤˤϤ��Ǥ�¸�ߤ��륭����ɤε������Ф��뽤���Υ�������ƨ���Ƥ���̵�̤˺ƹ��ۤ��Ƥ��ޤ���������˵��Ť��Ƥ������äȥ������夬�ä��Ϥ��ġ�

var (
	mKwControl sync.Mutex
	kwdList    KeywordArray

	mKwReplacer                  sync.RWMutex
	kwReplacer1st, kwReplacer2nd *strings.Replacer
)

func updateReplacer() {
	reps1 := make([]string, 0, len(kwdList)*2)
	reps2 := make([]string, 0, len(kwdList)*2)
	for i, k := range kwdList {
		if k.holder == "" {
			k.holder = fmt.Sprintf("isuda_%x", sha1.Sum([]byte(k.Key)))
			kwdList[i].holder = k.holder
		}

		reps1 = append(reps1, k.Key)
		reps1 = append(reps1, k.holder)

		reps2 = append(reps2, k.holder)
		reps2 = append(reps2, k.Link)
	}
	r1 := strings.NewReplacer(reps1...)
	r2 := strings.NewReplacer(reps2...)
	mKwReplacer.Lock()
	kwReplacer1st = r1
	kwReplacer2nd = r2
	mKwReplacer.Unlock()
}

func AddKeyword(key, link string) {
	k := Keyword{Key: key, Link: link}

	mKwControl.Lock()
	kwdList = append(kwdList, k)
	sort.Sort(kwdList)

	updateReplacer()
	mKwControl.Unlock()
}

func ReplaceKeyword(c string) string {
	mKwReplacer.RLock()
	r1 := kwReplacer1st
	r2 := kwReplacer2nd
	mKwReplacer.RUnlock()
	x := r1.Replace(c)
	x = html.EscapeString(x)
	return r2.Replace(x)
}

AddKeyword �ϥ�����ɤ�ݥ��Ȥ����Ȥ��ˡ� ReplaceKeyword �� htmlify ����ƤФ�ޤ�������� NewReplacer ��ƤӽФ�����������˺︺���뤳�Ȥ��Ǥ��ޤ�����

�����ѹ����������륿���ߥ󥰤ǡ� MySQL ����³����Ȥ��� '127.0.0.1' �������³�����Ĥ���Ƥ��ʤ��Ȥ������顼���Ǥ�褦�ˤʤäơ����⤷�Ƥʤ��Τ˲��줿�ɵ�: �����Υ����skip-name-resolve�����ä��Τ����������͡� Unix Domain Socket ��Ȥ��褦�ˤ��ޤ����� @kizkoh �ˤ�� nginx �� MySQL ������ (��Ū�ե������ nginx ���֤���) �䡢��������ˤ��ä��Х����٤����ꤷ�ơ�14:00������12������Ф��ޤ�����

isutar �� isuda �˥ޡ���

����� @mecha_g3 ��Ǥ������ʬ�Ǥ��� isutar �� isuda �����ߤ���JSON API��ȤäƤ����򤷤Ƥ�����ʬ�����äơ�2�ĤΥ��ץ��MySQL��1��Υޥ���˾�äƤ���ʾ崰����̵�̤ʤΤ�����1�ĤˤޤȤ�ޤ�������������Ͽ���Ƥʤ��ΤǤ�����������ǽ���åפ����Ϥ��Ǥ���

������������ƥ٥���򤫤���Ȥ��˥ϥޤä��Τ���DB���ͥ������Υǥåɥ��å��Ǥ�����Ȥ��2������1��Υޥ���ʤΤǡ�DB�ؤ���³��4�ܤ��פ�ʤ��������Ȼפ��Ĥġ�ǰ�Τ����8�ܤˤ��Ƥ��ޤ������Ȥ����������Τ褦�ʥ����ɤ�������8�ܤ��äƤ�­��ʤ��ʤäƤ��ޤ��ޤ�����

	rows, err := db.Query(fmt.Sprintf(
		"SELECT * FROM entry ORDER BY updated_at DESC LIMIT %d OFFSET %d",
		perPage, perPage*(page-1),
	))
	if err != nil && err != sql.ErrNoRows {
		panicIf(err)
	}
	entries := make([]*Entry, 0, 10)
	for rows.Next() {
		e := Entry{}
		err := rows.Scan(&e.ID, &e.AuthorID, &e.Keyword, &e.Description, &e.UpdatedAt, &e.CreatedAt)
		panicIf(err)
		e.Html = htmlify(w, r, e.Description)
		e.Stars = loadStars(e.Keyword)
		entries = append(entries, &e)
	}
	rows.Close()

���Υ����ɤϥȥåץڡ����Υϥ�ɥ顼�ΰ����Ǥ��� for rows.Next() �롼�פ���λ���� rows.Close() ���ƤФ��ޤ�DB����³�򰮤�ΤǤ��������Υ롼����� loadStars(e.Keyword) ����ʬ����Ǥ����SQL��ƤӽФ��Ƥ��ޤ��� ���ξ��֤ǡ� top �ڡ�����8������ǥ�����������ƥ롼�פγ�¦�Υ������¹Ԥ���ȡ�8�ܤ���³��Ȥ��ڤä����֤ˤʤꡢ�ɤ� goroutine ����¦�� loadStars() ��DB��³��̵�¤��ԤäƤ��ޤ����Ȥˤʤ�ޤ���

����夤�ƹͤ���Ф��������������Τ��ȤʤΤǤ������ǽ�� rows.Close() ��Ƥ�Ǥʤ���꤬�ɤ����ˤ���󤸤�ʤ�����õ����ä��ꡢ���η��Υͥ��Ȥ��ޤ������Ȥ˵��Ť����Ȥ���֤Ǥ�1goroutine�������DB��³����2�ܤˤʤ���������顢�������٤�;͵�ϸ������8�ܤˤ���������ɤʤ����פȹͤ��Ƥ��ޤä��ꤷ�Ƥ��ޤ��ޤ�����

��̡�����������к�������ȥͥ��Ȥ��٤��ΤǤϤʤ���ñ�˥��ͥ������ס���ο����ܤ�16�����䤷�������Ǥ����٥���ޡ��������ɤ�ɤ������پ夲�Ƥ��륿���פ��ä��餳��Ǥ���Ǥޤ�����

�����Ȥ��ơ� rows.Next() �롼����ǥͥ��Ȥ��ƥ������¹Ԥ���Τ�ñ��ɬ�פ���³�����ܤˤʤ�ʾ�ζ���������äƤ���Τǡ� rows.Close() �ޤǤν�����ñ��˥������̤Υե��å������ˤ��ޤ��礦��

Replacer �ι��ۤ� zero time cache ��

�ץ��ե�������äƤߤ��Ȥ����ޤ� Replacer �ι��ۤ��Ť��Τǡ��Ŀ�Ū�� zero time cache �ȸƤ�Ǥ��륤�ǥ������Ȥäư����˥���å���򤷤Ƥߤޤ������Υ����ɤ򸫤Ƥ���������

var (
	mKwControl sync.Mutex
	kwdList    KeywordArray

	mUpdateReplacer sync.Mutex
	repLastUpdated  time.Time
)

func AddKeyword(key, link string) {
	k := Keyword{Key: key, Link: link}
	mKwControl.Lock()
	kwdList = append(kwdList, k)
	mKwControl.Unlock()
	updateReplacer()
}

func updateReplacer() {
	now := time.Now()
	mUpdateReplacer.Lock()
	defer mUpdateReplacer.Unlock()

	if repLastUpdated.After(now) {
		return
	}
	repLastUpdated = time.Now()

	reps1 := make([]string, 0, len(kwdList)*2)
	reps2 := make([]string, 0, len(kwdList)*2)

	mKwControl.Lock()
	kws := kwdList[:]
	mKwControl.Unlock()

	sort.Sort(kws)
����// ... �ʹ� Replacer �ι��۽���
}

mUpdateReplacer �� repLastUpdated ���ɲä����ѿ��ǡ� updateReplacer() �� repLastUpdated = time.Now() �ޤǤ����ǥ�����ˤʤäƤ��ޤ���

�㤨�С��������A, B, C ���ۤ�Ʊ�����ɲä��줿�Ȥ��ޤ���

  1. �ޤ�A���ǽ�˥��å���������ơ� repLastUpdated = time.Now() ��¹Ԥ��� Replacer �ι��ۤ�Ϥ�ޤ���
  2. ³���ƥ������B��C���Ȥ�� mUpdateReplacer.Lock() ����ߤ��ޤ������å�ľ���˼������� now �� (1) �ǹ������� repLastUpdated ����̤��λ��֤ˤʤ�ޤ���
  3. �������A�� Replacer ��������λ���� mUpdateReplacer ����������ޤ����������B��������Ƥ��� goroutine �����Υ��å���������ޤ���
  4. ���å��������˼������� now �� repLastUpdated ���⿷�����Τǡ� if repLastUpdated.After(now) �����ˤʤ�ޤ��󡣤ʤΤ� Replacer ��ƹ��ۤ��ޤ����������A�򹹿������Ȥ��� kwdList �˥������B�����äƤ��ʤ��ä��ʸ�̩�ˤϥ����ߥ󥰰�¸�����äƤ�����ǽ���⤢��ˤΤǡ������ɬ�פʽ����Ǥ���
  5. ����˥������B�� Replacer ����������ꡢ�������C�� mUpdateReplacer �Υ��å���������ޤ������٤� if repLastUpdated.After(now) �����ˤʤ�Τǡ� Replacer �κƹ��ۤϥ����åפ���ޤ��� (4) �� Replacer ��ƹ��ۤ����Ȥ��ˤϳμ¤˥������C�� kwdList �����äƤ����Τǡ������åפ��Ƥ�����Ǥ���

��������������ɤ�������Ǥ�����Ǥ��ʤ����⤷��ޤ��󤬡����Ҥ��ä���ȥ����ɤȸ���٤����򤷤ƤߤƤ��������� ���Υ��ǥ������ISUCON�����ǤϤʤ����ºݤ�Go�ǹ���ǽ�ʥ����С���񤯤Ȥ������������Ǥ���

�ޤ����㤨�� MySQL ��ʣ���Υȥ�󥶥���������٤� fsync �ǥ��ߥåȤ��륰�롼�ץ��ߥåȤʤɡ���Ʊ���˼¹Ԥ����Ť�������Хå�������פȤ����Τ���������Υǥ�����ѥ�����ȸƤ�Ǥ��ɤ����餤����Ū���Ȼפ��ΤǤ������ɤʤ������Υѥ������̾����¸�ΤǤ����� @methane ���� Tweet �ʤɤǶ����Ƥ���������

�ž夲

�ޤ� top �ڡ����Υϥ�ɥ顼���Ť��ä��Τǡ��Ƥ� zero time cache �ѥ������ȤäƤ���˥ȥåץڡ��������Ƥμ������Ψ�����ޤ���

���������ΤȤ��ϸ�Ψ�������μ��ᤵ��ͥ�褷�ơ� zero time cache �ѥ������ȥåץڡ����Υϥ�ɥ����ľ�ܼ������Ƥ��ޤ��ޤ�������¤ˤ���ǽ���夷�ޤ�����������⥭����ɤι���¦�˽�������äƤ����Ф�äȷ�Ū����ǽ���夬�����᤿�Ϥ��Ǥ���

�Ǹ�ˡ����ץ�δĶ��ѿ��� GOGC=400 �����ꤷ���ץ��ե�������˥���󥰤�nginx�Υ��������������ڤꡢ�٥��ȥ�������Ф��ޤ� enqueue �����¹Ԥ��ƽ�λ���ޤ�����

�ͻ�

POST /keyword �Υ���������������ɤ��ɲä����ǤϤʤ������ˤ�Ȥ��Ƥ��뤳�Ȥ�ƨ���Ƥ��ơ����ֽŤ� Replacer �κƹ��ۤ�ɬ�װʾ�˼¹Ԥ��Ƥ��ޤäƤ����Τ�����ޤ�ޤ���

�ޤ�����˽񤤤��褦�˥ȥåץڡ��������Ƥι����⡢ GET / �ǤϤʤ� POST /keyword �ǹԤ��Ф�äȷ�Ū�ʥ��������åפ��Ǥ����Ǥ��礦��

����ˡ����������桼�����Υȥåץڡ�������Ū�ե�����˽񤭽Ф��Ƥ��ޤä� nginx ��ľ���֤�����Τ⡢ISUCON���ѽФι�άˡ�ǻ�����Ƚ�äƤ����Ϥ��ǡ�����������֤⤢�ä��Ϥ��ʤΤˤ�륿���ߥ󥰤�路�Ƥ��ޤ��ޤ�����

�����������Ǥ��Ƥ���С��٥���ޡ������μ�������Ǥ������ܤ�50�����β�ǽ���⤢�ä����⤷��ޤ���

����

���ޤǤ���ǯͽ���ϰ�ͤ���äƾ���ȴ���Ƥ����ΤǤ�������ǯ��ͽ���Υܥ�塼�ब�����Ʊ�����餤���äƥ��Ĥ��ä��Τǡ���ǯ��ͽ�������������臘���Ȥˤ��ޤ�����

��ľ�����Ϥ��ΥХ������ä��Υ֥����Ǥ�ľ�äƤʤ��ä��ס֥ץ��ե����뤬�㤦���ΥХ��ʥ꡿�������򻲾Ȥ��Ƥ����⤷��ʤ��Ƥ��ƥ��Ƥˤʤ�ʤ��׷ϤΥȥ�֥�Ϥ��ä���ΤΡ���ʬ�����ץ��񤯤Ȥ��˿��ۻ���˺��ƽ���Ǥ����ꡢ�դ˼�����Ǥ���Ƥ�֤˼�ʬ�ϵٷƤ���������夤�Ƹ�ľ���򤷤���Ǥ��ơ�����Ū����٤������˸��ꡢ;͵����ä�ͥ�����廊���Ȼפ��ޤ���

�����ǡ�Azure�˴���Ƥ��ʤ��ä����Ȥ䡢14���᤮�˼�ä����Ƥ�0���ʳ��Υ����������λ������ǥȥĥȥåפǡ����������ۤܤ��ä�2�̤Υ��֥륹�����򥭡��פ��Ƥ������Ȥ⤢�äơ��ݼ�Ū�ˤʤꤹ�������Ϥ�Ф��ڤ줿�Ȥϸ����񤤤Ǥ���

����Ǥ����Ϥ�Ф���褦�ˡ��⤦������������������Ķ����ۤ�������ŤͤƹԤ��ޤ���

����Ǥϡ��辡�ʽмԤΤߤʤ��󡢷辡�Ǥ��񤤤��ޤ��礦�� ���ġ�����ԤΤߤʤ���Ϥ�����ͤǤ������辡�����³�����ꤤ�������ޤ���

songofacandy at 16:25��Comments(3)��TrackBack(0)��ISUCON | golang

�ȥ�å��Хå�URL

���ε����ؤΥ�����

1. Posted by hiro   2016ǯ09��20�� 21:06
���������ƥ��˥å���ͭ���Ǥ��͡�
�����餷�������򤢤꤬�Ȥ��������ޤ�����
�������󲽤Υƥ��˥å�����ʬ�Ǥ�time.Now��Ȥ�����len(kwdList)��Ȥ�����ʬ����䤹���󤸤�ʤ����Ȼפ��ޤ���
Go��time.Now�ϼ����Ȥ��Ƥ�monotonic���Ȥϻפ��ΤǤ�����̤��ǧ�ˡ����ʤ��Ȥ��ɥ�����Ⱦ���ݾڤ���Ƥ��ʤ��褦�˻פ��ޤ���
�����reps1, reps2�ν��������len(kwdList)����Ѥ��Ƥ��ޤ����������lock�Ǽ�ä������ɤ��ΤǤ�? append��len�˶����̵���ΤǤ��礦����
2. Posted by methane   2016ǯ09��23�� 16:08
len(kwdList) ����ˤϥ��å�����ʤ��Ȥ����ʤ��Τȡ�Blog�ǤϾ�ά���Ƥ��ޤ���
�ºݤˤϥ�����ɤ�������ؿ��⤢��Τ��ɲäȺ����1�Ĥ��ļ¹Ԥ�����
len(kwdList) ��Ʊ���ˤʤäƤ��ޤ��ޤ���
�ޤ�������� kwdList �˷׻���ɬ�פʻ񸻤����äƤ��ޤ����������Υ��ǥ����������Ū��
�ǡ����١����˽Ť���������ꤲ��ʤɤǤ����Ѳ�ǽ�ǡ� time.Now() ��Ȥä��ۤ���
�����ɥ��˥ڥåȤȤ��Ƥκ����������⤤�Ǥ���

time.Now() �ϥ�Υȥ˥å��ˤϤʤäƤ��ޤ��� Google ���Ǥϡ����פ򴬤��᤹�ۤ���
�����Ȥ������Ȥˤʤ�ΤǤ��礦�͡�
�ʤΤǼ�ư�ǻ��׹�碌�򤹤������������դ�ɬ�פǤ���
�ʻ�Ϥ����դ�����˾ܤ����ʤ��ΤǼ��䤵��Ƥ��������ޤ��󤬡�
3. Posted by hiro   2016ǯ09��23�� 23:22
> len(kwdList) ����ˤϥ��å�����ʤ��Ȥ����ʤ��Τȡ�Blog�ǤϾ�ά���Ƥ��ޤ���
�ºݤˤϥ�����ɤ�������ؿ��⤢��Τ��ɲäȺ����1�Ĥ��ļ¹Ԥ�����
len(kwdList) ��Ʊ���ˤʤäƤ��ޤ��ޤ���

�ʤ�ۤɡ�ABA����ˤʤäƤ��ޤ���Ǥ��͡�

> �ޤ�������� kwdList �˷׻���ɬ�פʻ񸻤����äƤ��ޤ����������Υ��ǥ����������Ū��
�ǡ����١����˽Ť���������ꤲ��ʤɤǤ����Ѳ�ǽ�ǡ� time.Now() ��Ȥä��ۤ���
�����ɥ��˥ڥåȤȤ��Ƥκ����������⤤�Ǥ���

�Τ��ˤ����̤�Ǥ��͡�

> time.Now() �ϥ�Υȥ˥å��ˤϤʤäƤ��ޤ��� Google ���Ǥϡ����פ򴬤��᤹�ۤ���
�����Ȥ������Ȥˤʤ�ΤǤ��礦�͡�
�ʤΤǼ�ư�ǻ��׹�碌�򤹤������������դ�ɬ�פǤ���
�ʻ�Ϥ����դ�����˾ܤ����ʤ��ΤǼ��䤵��Ƥ��������ޤ��󤬡�

���������ʤ�Ǥ��͡��μ¤˥��ߥåȤ��������ϼ�����syscall�䥫���󥿡��ʤɤ��Ѱդ���ɬ�פ�ͭ��Ȥ������Ǥ����͡��ͤ�褯ʬ���äƤ��ޤ��󤬡�

���������꤬�Ȥ��������ޤ�����

���ε����˥����Ȥ���

̾��:
URL:
  ����òµ­²ï¿½: ɾ��: ��    ��
 
 
 
Blog�⸡��
�ǿ�����
Archives
���Υ֥����ˤĤ���
DSAS�Ȥϡ�KLab �����ۤ����Ѥ��Ƥ��륳��ƥ�ĥ����ӥ��Ѥ�Linux�١����Υ���ե�Ǥ�������5����Υǡ������󥿤ˤƹ��ۤ������Ѥ��Ƥ��ޤ������桹��DSAS����Ȥ��䤹�����������ˡ������Ƥ����ϤDZ��ѤǤ��뤳�Ȥ��ܻؤ��ơ��������ɤ˶Ф���Ǥ��ޤ���
���Υ֥����Ǥϡ������ DSAS �ǻȤäƤ��뵻�ѤξҲ�䡢�¸����Ƥߤ���̤���𡢥ȥ�֥�˴������ޤ줿���ηи��̤ʤɡ���������������������򿥤�ޤ��ƾҲ𤷤Ƥ��������Ȼפ��ޤ���
�ǿ�������
<%==comments[n].author%>
<% } %>