Groupcache: ์บ์‹œ ํ•ญ๋ชฉ์„ ์ž์ฃผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•œ ๋ชจ๋ฒ” ์‚ฌ๋ก€

์— ๋งŒ๋“  2015๋…„ 03์›” 08์ผ  ยท  9์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: golang/groupcache

๋‚ด ์งˆ๋ฌธ์€ https://github.com/golang/groupcache/issues/3 ๋ฌธ์ œ์™€ ์•ฝ๊ฐ„ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค

ํ˜„์žฌ ๋‹จ์ผ ์ธ์Šคํ„ด์Šค์˜ go ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ RAM์—์„œ ๊ด€๋ฆฌ๋˜๋Š” ๋งต์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ™•์žฅ์„ ์œ„ํ•ด ์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค ๊ฐ„์— ์ด ๋งต์„ ๊ณต์œ ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ์ด๋ฏธ ํ”ผ์–ด ์ธ์Šคํ„ด์Šค ๊ฒ€์ƒ‰์„ ์œ„ํ•ด ์˜์‚ฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ํ˜„์žฌ redis๋กœ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  ์žˆ์ง€๋งŒ ๊ฐ ์‹œ์Šคํ…œ์˜ RAM์„ ํ™œ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์‚ฌ์‹ค์— ๋งŒ์กฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์€๋‹‰์ฒ˜). ์ด๊ฒƒ์ด ๋‚ด๊ฐ€ groupcache๋ฅผ ์ข‹์•„ํ•˜๋Š” ํ•œ ๊ฐ€์ง€ ์ด์œ ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ œ์•ฝ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด ์ง€๋„๋Š” ํ•ญ์ƒ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค(http๋ฅผ ํ†ตํ•ด ์ง€๋„๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ผ๋Š” ์š”์ฒญ์„ ๋ฐ›๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค). ๋”ฐ๋ผ์„œ ๋งต์— ์žˆ๋Š” ํ‚ค K1์˜ ๊ฒฝ์šฐ m[K1]์ด ๋งค์šฐ ์ž์ฃผ(1์ดˆ ๋˜๋Š” ๊ทธ ์ดํ•˜๋กœ) ์—…๋ฐ์ดํŠธ๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋‚ด ์งˆ๋ฌธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. ๋‚ด๊ฐ€ ์ž˜๋ชป๋œ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ ํƒํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ? Redis ๋˜๋Š” memecached์™€ ๊ฐ™์€ ๊ฒƒ์„ ๋Œ€์‹  ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?
  2. groupcache๊ฐ€ ๋‚ด ์‚ฌ์šฉ ์‚ฌ๋ก€์— ์ ํ•ฉํ•œ ์†”๋ฃจ์…˜์ธ ๊ฒฝ์šฐ ์ง€์†์ ์œผ๋กœ ์ œ๊ฑฐ ๋ฐ ์ถ”๊ฐ€(์˜ˆ: LRU ์บ์‹œ)ํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด ๋” ํ˜„๋ช…ํ•œ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ?

๊ฐ์‚ฌ ํ•ด์š”!

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์•ˆ๋…•ํ•˜์„ธ์š” @orcaman์ž…๋‹ˆ๋‹ค. ์ €๋Š” ๋˜ ๋‹ค๋ฅธ Groupcache ์‚ฌ์šฉ์ž์ง€๋งŒ ๊ท€ํ•˜์˜ ์งˆ๋ฌธ์— ๋‹ต๋ณ€ํ•  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์šฐ์„ , ๊ธฐ์–ตํ•ด์•ผ ํ•  ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋‘ ๊ฐ€์ง€ ์‚ฌํ•ญ์€ Groupcache๊ฐ€ ์ฝ๊ธฐ ์ „์šฉ ์บ์‹œ๋ผ๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ๊ทธ ์•ˆ์— ์žˆ๋Š” ํ‚ค๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‘˜์งธ, ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋Š” ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ณ  ๋งŒ๋ฃŒ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ถ„์‚ฐ LRU์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ‚ค๋ฅผ ์ž์ฃผ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ GC๋Š” ์ข‹์€ ์˜ต์…˜์ด ์•„๋‹™๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ํ‚ค ์กฐ์ž‘์œผ๋กœ ๊ธฐ์กด ๋ฐ์ดํ„ฐ์˜ ๋งŒ๋ฃŒ ๋˜๋Š” ๋ณ€๊ฒฝ์„ ์—๋ฎฌ๋ ˆ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ œ ๊ฒฝ์šฐ์—๋Š” ํ‚ค์— ๋Œ€ํ•ด ์•ฝ 1์‹œ๊ฐ„์˜ ๋งŒ๋ฃŒ๊ฐ€ ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ํ•œ ๊ฒƒ์€ ๋‚ด๊ฐ€ ์–ป์œผ๋ ค๋Š” ๋ชจ๋“  ํ‚ค์— ๋‹ค์Œ ๋ผ์šด๋“œ ์‹œ๊ฐ„์˜ ํƒ€์ž„ ์Šคํƒฌํ”„๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ•œ ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ๋‚ด ์•ฑ์—์„œ ํ‚ค์˜ ์ด ๋ถ€๋ถ„์ด ๋ณ€๊ฒฝ์„ ์š”์ฒญํ•˜๊ณ  Groupcache์˜ ๊ด€์ ์—์„œ ์ƒˆ ํ‚ค๋ฅผ ์š”์ฒญํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์ „ ๊ฒƒ์€ ๋” ์ด์ƒ ์•„๋ฌด๋„ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— LRU ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ํ†ตํ•ด ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ง€์†์ ์œผ๋กœ ํ‚ค๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๊ณ  ๋‘ ๋ฒˆ์งธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์œผ๋กœ ๋งŒ๋ฃŒ๋˜์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ redis ๋˜๋Š” memcache๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚˜์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋” ๋งŽ์€ ์„œ๋ฒ„๋กœ ํ™•์žฅํ•˜๊ธฐ ์‰ฝ๊ธฐ ๋•Œ๋ฌธ์— Memcache๋ฅผ ์„ ํƒํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  9 ๋Œ“๊ธ€

์•ˆ๋…•ํ•˜์„ธ์š” @orcaman์ž…๋‹ˆ๋‹ค. ์ €๋Š” ๋˜ ๋‹ค๋ฅธ Groupcache ์‚ฌ์šฉ์ž์ง€๋งŒ ๊ท€ํ•˜์˜ ์งˆ๋ฌธ์— ๋‹ต๋ณ€ํ•  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์šฐ์„ , ๊ธฐ์–ตํ•ด์•ผ ํ•  ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋‘ ๊ฐ€์ง€ ์‚ฌํ•ญ์€ Groupcache๊ฐ€ ์ฝ๊ธฐ ์ „์šฉ ์บ์‹œ๋ผ๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ๊ทธ ์•ˆ์— ์žˆ๋Š” ํ‚ค๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‘˜์งธ, ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋Š” ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ณ  ๋งŒ๋ฃŒ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ถ„์‚ฐ LRU์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ‚ค๋ฅผ ์ž์ฃผ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ GC๋Š” ์ข‹์€ ์˜ต์…˜์ด ์•„๋‹™๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ํ‚ค ์กฐ์ž‘์œผ๋กœ ๊ธฐ์กด ๋ฐ์ดํ„ฐ์˜ ๋งŒ๋ฃŒ ๋˜๋Š” ๋ณ€๊ฒฝ์„ ์—๋ฎฌ๋ ˆ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ œ ๊ฒฝ์šฐ์—๋Š” ํ‚ค์— ๋Œ€ํ•ด ์•ฝ 1์‹œ๊ฐ„์˜ ๋งŒ๋ฃŒ๊ฐ€ ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ํ•œ ๊ฒƒ์€ ๋‚ด๊ฐ€ ์–ป์œผ๋ ค๋Š” ๋ชจ๋“  ํ‚ค์— ๋‹ค์Œ ๋ผ์šด๋“œ ์‹œ๊ฐ„์˜ ํƒ€์ž„ ์Šคํƒฌํ”„๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ•œ ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ๋‚ด ์•ฑ์—์„œ ํ‚ค์˜ ์ด ๋ถ€๋ถ„์ด ๋ณ€๊ฒฝ์„ ์š”์ฒญํ•˜๊ณ  Groupcache์˜ ๊ด€์ ์—์„œ ์ƒˆ ํ‚ค๋ฅผ ์š”์ฒญํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์ „ ๊ฒƒ์€ ๋” ์ด์ƒ ์•„๋ฌด๋„ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— LRU ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ํ†ตํ•ด ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ง€์†์ ์œผ๋กœ ํ‚ค๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๊ณ  ๋‘ ๋ฒˆ์งธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์œผ๋กœ ๋งŒ๋ฃŒ๋˜์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ redis ๋˜๋Š” memcache๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚˜์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋” ๋งŽ์€ ์„œ๋ฒ„๋กœ ํ™•์žฅํ•˜๊ธฐ ์‰ฝ๊ธฐ ๋•Œ๋ฌธ์— Memcache๋ฅผ ์„ ํƒํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

@dvirsky ๊ฐ์‚ฌ
๋‚˜๋Š” ๊ทธ๋ฆผ์„ ์–ป๋Š”๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

๋ฌธ์ œ๋Š” ์—†์ง€๋งŒ ์ด์Šค๋ผ์—˜ ํ˜„์žฅ์—์„œ Groupcache๋ฅผ ๋” ๋งŽ์ด ์ฑ„ํƒํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ž์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํšŒ์‚ฌ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ? :)

์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋ฉด์„œ ๊ณ„์† ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :-)
๋‚˜๋Š” ์ด์Šค๋ผ์—˜ ํ˜„์žฅ์—์„œ ๋งŽ์€ ๊ณ ํผ๋ฅผ ์•Œ์ง€ ๋ชปํ•˜๋ฏ€๋กœ Groupcache ์‚ฌ์šฉ์ž์— ๋Œ€ํ•ด ์•Œ์ง€ ๋ชปํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‹ค๊ฐ€์˜ค๋Š” go ๋ชจ์ž„์—์„œ ์—ฌ๋Ÿฌ๋ถ„์„ ๋งŒ๋‚˜๊ธฐ๋ฅผ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค.

๋ฉ‹์žˆ๋Š”. ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ์„ ๋‚˜๊ฐ€๋Š” HTTP ์บ์‹œ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค(์ฆ‰, ์šฐ๋ฆฌ๊ฐ€ ์ œ3์ž์—๊ฒŒ ๋งŒ๋“œ๋Š” ์บ์‹œ ์š”์ฒญ). ์šฐ๋ฆฌ๋Š” ์–ธ์  ๊ฐ€ ์ด ๊ตฌํ˜„์„ ์—ด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
Go ๋ชจ์ž„์— ๋Œ€ํ•ด ํ™•์‹ ์ด ์„œ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์„ฑ๊ณตํ•  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

@dvirsky ๋ฐ @bradfitz - ์ œ ์‚ฌ์šฉ ์‚ฌ๋ก€์—์„œ๋Š” TTL[ํ˜„์žฌ ์‹œ๊ฐ„ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ํ‚ค์— ์ถ”๊ฐ€ํ•  ๊ณ„ํš]์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์งˆ๋ฌธ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

1. LRU ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ ์–ด๋–ป๊ฒŒ ํ‚ค๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๊นŒ? ํ‚ค๊ฐ€ [๋งŒ๋ฃŒ๋œ ๊ฒƒ์œผ๋กœ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด] ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ์ตœ์†Œ ๊ธฐ๊ฐ„์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?
2. ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ ์บ์‹œ์— ํ• ๋‹น๋œ ๋ฉ”๋ชจ๋ฆฌ์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ํ• ๋‹น๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ดˆ๊ณผํ•˜๋ฉด ๊ฐ€์žฅ ์ตœ๊ทผ์— ์‚ฌ์šฉํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ œ๊ฑฐ๋ฉ๋‹ˆ๊นŒ? ์ด ๊ฒฝ์šฐ ํ˜„์žฌ ์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ฑฐํ•˜์ง€ ์•Š๋„๋ก ๋ฉ”๋ชจ๋ฆฌ ๊ท ํ˜•์„ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์ด์— ๋Œ€ํ•ด ๋ช‡ ๊ฐ€์ง€ ์ œ์•ˆ์„ ํ•ด์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

@dvirsky ์ข‹์€ ๋‹ต๋ณ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํ•ญ๋ชฉ์ด ๋™์‹œ์— "์ œ๊ฑฐ"๋  ๋•Œ ๋งค์‹œ๊ฐ„ CPU ๋ฐ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์•ฝ๊ฐ„ ์ŠคํŒŒ์ดํฌํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@qbig ๋งŒ๋ฃŒ๋ฅผ ๊ตฌํ˜„ํ•œ ๋ฐฉ๋ฒ•์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ํ•œ ์ผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

// turn a ttl into an expiration timestamp, using discrete time windows.
// i.e. ask for an hour and get the nearest hour end as the expiration point.
//
// We pad these discrete boundaries  pseudo random margin (based on hashing the key)
// to avoid hitting the cache too hard if all requests expire at once
//
// This can be seconds from now even if you cache for days :)
func calcExpiration(ttl int64, key string, now int64) int64 {
    //we calculate the non discrete expiration, relative to current time
    expires := now

    var padding int64 = 0
    if ttl > 0 {
        // now we want to pad it so we'll no expire all reqeusts for a given time window at once
        // to be consistent, the seed of the padding is a hash on the url
        h := fnv.New32a()
        h.Write([]byte(key))
        padding = int64(h.Sum32()) % ttl

               // not sure this is correct - I wrote it long ago :)
        expires += (ttl - (expires % ttl)) - padding
        if expires < now {
            expires += ttl
        }

    }

    return expires

}

@orcaman ์€ https://tarantool.org/๋ฅผ ์‚ดํŽด๋ณด์‹ญ์‹œ์˜ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰

๊ด€๋ จ ๋ฌธ์ œ

cowboyrushforth picture cowboyrushforth  ยท  5์ฝ”๋ฉ˜ํŠธ

AlexanderChen1989 picture AlexanderChen1989  ยท  6์ฝ”๋ฉ˜ํŠธ

yml picture yml  ยท  3์ฝ”๋ฉ˜ํŠธ

abennett picture abennett  ยท  3์ฝ”๋ฉ˜ํŠธ

elimisteve picture elimisteve  ยท  7์ฝ”๋ฉ˜ํŠธ