เราทุกคนรักเรื่องราว เราชอบนั่งรอบกองไฟและพูดคุยเกี่ยวกับชัยชนะ การต่อสู้ หรือประสบการณ์การทำงานของเราในอดีต
วันนี้ก็เป็นเพียงวันดังกล่าว และถึงแม้ตอนนี้คุณจะไม่อยู่ในกองไฟ เราก็มีเรื่องราวมาแจ้งให้คุณทราบ เรื่องราวที่เราเริ่มต้นทำงานกับพื้นที่เก็บข้อมูลบน Tarantool
กาลครั้งหนึ่ง บริษัทของเรามี "เสาหิน" สองอันและ "เพดาน" หนึ่งอันสำหรับทุกคน ซึ่งเสาหินเหล่านี้ค่อยๆ เข้ามาใกล้อย่างช้าๆ แต่แน่นอน ซึ่งจำกัดการบินของบริษัทของเรา การพัฒนาของเรา และมีความเข้าใจที่ชัดเจนว่า วันหนึ่งเราจะชนเพดานนี้อย่างแรง
ปัจจุบันเป็นอุดมการณ์ที่แพร่หลายในการแบ่งแยกทุกสิ่งและทุกคนออกจากอุปกรณ์ไปจนถึงตรรกะทางธุรกิจ ด้วยเหตุนี้ เราจึงมี DC สองตัวที่ในทางปฏิบัติแล้วมีความเป็นอิสระในระดับเครือข่าย แล้วทุกอย่างก็แตกต่างไปจากเดิมอย่างสิ้นเชิง
ปัจจุบันมีเครื่องมือและเครื่องมือมากมายสำหรับการเปลี่ยนแปลงในรูปแบบของ CI/CD, K8S ฯลฯ ในยุค "เสาหิน" เราไม่ต้องการคำต่างประเทศมากนัก เพียงแก้ไข "ที่เก็บข้อมูล" ในฐานข้อมูลก็เพียงพอแล้ว
แต่เวลาก้าวไปข้างหน้า และจำนวนคำขอก็เคลื่อนไปข้างหน้าตามไปด้วย ซึ่งบางครั้งทำให้ RPS เกินความสามารถของเรา จากการที่ประเทศ CIS เข้าสู่ตลาด โหลดในโปรเซสเซอร์ฐานข้อมูลของเสาหินแรกไม่ลดลงต่ำกว่า 90% และ RPS ยังคงอยู่ที่ระดับ 2400 และสิ่งเหล่านี้ไม่ได้เป็นเพียงตัวเลือกขนาดเล็กเท่านั้น แต่ยังมีข้อความค้นหาจำนวนมากด้วย การตรวจสอบและเข้าร่วมจำนวนมากที่สามารถทำงานได้เกือบครึ่งหนึ่งของข้อมูลเทียบกับพื้นหลังของ IO ขนาดใหญ่
เมื่อยอดขาย Black Friday อย่างเต็มรูปแบบเริ่มปรากฏขึ้น - และ Wildberries เป็นหนึ่งในคนกลุ่มแรก ๆ ที่ถือครองพวกเขาในรัสเซีย - สถานการณ์กลายเป็นเรื่องน่าเศร้าอย่างยิ่ง ท้ายที่สุดแล้วภาระในวันดังกล่าวเพิ่มขึ้นสามครั้ง
โอ้ “ยุคเสาหิน” เหล่านี้! ฉันแน่ใจว่าคุณเคยประสบสิ่งที่คล้ายกันและคุณยังไม่เข้าใจว่าสิ่งนี้จะเกิดขึ้นกับคุณได้อย่างไร
คุณทำอะไรได้บ้าง - แฟชั่นนั้นมีอยู่ในเทคโนโลยี ประมาณ 5 ปีที่แล้ว เราต้องคิดใหม่เกี่ยวกับม็อดเหล่านี้ในรูปแบบของไซต์ที่มีอยู่ในเซิร์ฟเวอร์ .NET และ MS SQL ซึ่งเก็บตรรกะทั้งหมดของไซต์อย่างระมัดระวัง ฉันเก็บมันไว้อย่างระมัดระวังจนการเห็นหินใหญ่ก้อนเดียวกลายเป็นความสุขที่ยาวนานและไม่ใช่เรื่องง่ายเลย
พูดนอกเรื่องเล็ก ๆ
ในกิจกรรมต่างๆ ฉันพูดว่า: "ถ้าคุณไม่เห็นเสาหิน คุณก็จะไม่เติบโต!" ฉันสนใจความคิดเห็นของคุณเกี่ยวกับเรื่องนี้ โปรดเขียนไว้ในความคิดเห็น
เสียงฟ้าร้อง
กลับมาที่ "กองไฟ" ของเราอีกครั้ง เพื่อกระจายโหลดของฟังก์ชัน "เสาหิน" เราตัดสินใจแบ่งระบบออกเป็นไมโครเซอร์วิสโดยอิงตามเทคโนโลยีโอเพ่นซอร์ส เพราะอย่างน้อยที่สุดก็มีราคาถูกกว่าเมื่อปรับขนาด และเรามีความเข้าใจ 100% ว่าเราจะต้องขยายขนาด (และอีกมาก) ท้ายที่สุดแล้วในเวลานั้นก็สามารถเข้าสู่ตลาดของประเทศเพื่อนบ้านได้ และจำนวนการลงทะเบียนตลอดจนจำนวนคำสั่งซื้อก็เริ่มเติบโตอย่างแข็งแกร่งยิ่งขึ้น
หลังจากวิเคราะห์ตัวเลือกแรกๆ ที่เปลี่ยนจากบริการขนาดใหญ่ไปสู่ไมโครเซอร์วิส เราพบว่า 80% ของการเขียนในนั้นมาจากระบบแบ็คออฟฟิศ และการอ่านจากแผนกต้อนรับ ก่อนอื่นสิ่งนี้เกี่ยวข้องกับระบบย่อยที่สำคัญสองสามระบบสำหรับเรา - ข้อมูลผู้ใช้และระบบสำหรับการคำนวณต้นทุนสุดท้ายของสินค้าตามข้อมูลเกี่ยวกับส่วนลดและคูปองของลูกค้าเพิ่มเติม
เยื้อง ตอนนี้มันน่ากลัวที่จะจินตนาการ แต่นอกเหนือจากระบบย่อยที่กล่าวมาข้างต้น แคตตาล็อกผลิตภัณฑ์ ตะกร้าสินค้าของผู้ใช้ ระบบค้นหาผลิตภัณฑ์ ระบบกรองสำหรับแคตตาล็อกผลิตภัณฑ์ และระบบการแนะนำประเภทต่างๆ ก็ถูกลบออกจากเสาหินของเราด้วย สำหรับการทำงานของแต่ละระบบนั้น มีระบบที่ปรับแต่งให้แคบแยกประเภทกัน แต่กาลครั้งหนึ่งพวกเขาทั้งหมดอาศัยอยู่ใน "บ้าน" เดียว
เราวางแผนที่จะถ่ายโอนข้อมูลเกี่ยวกับลูกค้าของเราไปยังระบบชาร์ดทันที การยกเลิกฟังก์ชันในการคำนวณต้นทุนสุดท้ายของสินค้าจำเป็นต้องมีความสามารถในการปรับขนาดที่ดีในการอ่าน เนื่องจากทำให้เกิดโหลด RPS มากที่สุดและเป็นวิธีการที่ยากที่สุดในการนำไปใช้กับฐานข้อมูล (ข้อมูลจำนวนมากเกี่ยวข้องกับกระบวนการคำนวณ)
ด้วยเหตุนี้ เราจึงได้รูปแบบที่เหมาะกับ Tarantool เป็นอย่างดี
ในเวลานั้น สำหรับการทำงานของไมโครเซอร์วิส ได้มีการเลือกรูปแบบการทำงานกับศูนย์ข้อมูลหลายแห่งบนเครื่องเสมือนและฮาร์ดแวร์ ดังที่แสดงในภาพ ตัวเลือกการจำลองแบบ Tarantool ถูกนำมาใช้ทั้งในโหมดมาสเตอร์-มาสเตอร์และมาสเตอร์-สเลฟ
สถาปัตยกรรม. ตัวเลือก 1. บริการผู้ใช้
ในปัจจุบัน มีส่วนแบ่งข้อมูล 24 ส่วน แต่ละส่วนมี 2 อินสแตนซ์ (หนึ่งอินสแตนซ์สำหรับ DC แต่ละตัว) ทั้งหมดอยู่ในโหมดมาสเตอร์-มาสเตอร์
ด้านบนของฐานข้อมูลคือแอปพลิเคชันที่เข้าถึงแบบจำลองฐานข้อมูล แอปพลิเคชันทำงานร่วมกับ Tarantool ผ่านไลบรารีแบบกำหนดเองของเรา ซึ่งใช้งานอินเทอร์เฟซไดรเวอร์ Tarantool Go เธอเห็นแบบจำลองทั้งหมดและสามารถทำงานร่วมกับอาจารย์เพื่ออ่านและเขียนได้ โดยพื้นฐานแล้ว จะใช้โมเดลชุดเรพลิกา ซึ่งจะเพิ่มตรรกะสำหรับการเลือกเรพลิกา ดำเนินการลองใหม่ เซอร์กิตเบรกเกอร์ และขีดจำกัดอัตรา
ในกรณีนี้ คุณสามารถกำหนดค่านโยบายการเลือกเรพลิกาในบริบทของชาร์ดได้ ตัวอย่างเช่น วงเวียน.
สถาปัตยกรรม. ตัวเลือกที่ 2 บริการคำนวณต้นทุนสุดท้ายของสินค้า
ไม่กี่เดือนที่ผ่านมา คำขอส่วนใหญ่ในการคำนวณต้นทุนสุดท้ายของสินค้าไปที่บริการใหม่ ซึ่งตามหลักการแล้วทำงานโดยไม่มีฐานข้อมูล แต่เมื่อไม่นานมานี้ทุกอย่างได้รับการประมวลผล 100% โดยบริการที่มี Tarantool ภายใต้ประทุน
ฐานข้อมูลบริการประกอบด้วย 4 ต้นแบบที่ตัวซิงโครไนซ์รวบรวมข้อมูล และต้นแบบการจำลองแต่ละอันจะกระจายข้อมูลไปยังแบบจำลองแบบอ่านอย่างเดียว ต้นแบบแต่ละคนมีแบบจำลองดังกล่าวประมาณ 15 ชิ้น
ในรูปแบบแรกหรือในรูปแบบที่สอง ถ้า DC ตัวใดตัวหนึ่งไม่พร้อมใช้งาน แอปพลิเคชันสามารถรับข้อมูลในรูปแบบที่สองได้
เป็นที่น่าสังเกตว่าการจำลองแบบใน Tarantool ค่อนข้างยืดหยุ่นและสามารถกำหนดค่าได้ในขณะรันไทม์ ในระบบอื่นมีปัญหาเกิดขึ้น ตัวอย่างเช่น การเปลี่ยนพารามิเตอร์ max_wal_senders และ max_replication_slots ใน PostgreSQL จำเป็นต้องรีสตาร์ทวิซาร์ด ซึ่งในบางกรณีอาจนำไปสู่การตัดการเชื่อมต่อระหว่างแอปพลิเคชันและ DBMS
แสวงหาแล้วคุณจะพบ!
ทำไมเราไม่ทำ “แบบคนปกติ” แต่เลือกวิธีที่ผิดปรกติล่ะ? ขึ้นอยู่กับสิ่งที่ถือว่าเป็นเรื่องปกติ โดยทั่วไปแล้ว ผู้คนจำนวนมากสร้างคลัสเตอร์จาก Mongo และกระจายไปยัง DC ที่กระจายตามภูมิศาสตร์สามแห่ง
ในเวลานั้น เรามีโครงการ Redis สองโครงการแล้ว อย่างแรกคือแคช และอย่างที่สองเป็นที่เก็บข้อมูลถาวรสำหรับข้อมูลที่ไม่สำคัญเกินไป มันค่อนข้างยากสำหรับเขา ส่วนหนึ่งเกิดจากความผิดของเรา บางครั้งคีย์ก็มีปริมาณค่อนข้างมาก และในบางครั้งไซต์ก็ไม่ค่อยดีนัก เราใช้ระบบนี้ในเวอร์ชันมาสเตอร์-สเลฟ และมีหลายกรณีที่บางสิ่งบางอย่างเกิดขึ้นกับต้นแบบและการจำลองล้มเหลว
นั่นคือ Redis เหมาะสำหรับงานไร้สัญชาติ ไม่ใช่งานเก็บสถานะ โดยหลักการแล้ว อนุญาตให้แก้ไขปัญหาส่วนใหญ่ได้ แต่เฉพาะในกรณีที่เป็นโซลูชันคีย์-ค่าที่มีดัชนีคู่กัน แต่ Redis ในเวลานั้นค่อนข้างเศร้ากับความพากเพียรและการจำลองแบบ นอกจากนี้ยังมีข้อร้องเรียนเกี่ยวกับประสิทธิภาพการทำงาน
เราคิดถึง MySQL และ PostgreSQL แต่อันแรกไม่ตรงกับเราเลย และอันที่สองเป็นผลิตภัณฑ์ที่ค่อนข้างซับซ้อนในตัวเอง และการสร้างบริการง่ายๆ ขึ้นมานั้นไม่เหมาะสม
เราลองใช้ RIAK, Cassandra หรือแม้แต่ฐานข้อมูลกราฟ สิ่งเหล่านี้ล้วนเป็นโซลูชันเฉพาะที่ไม่เหมาะกับบทบาทของเครื่องมือสากลทั่วไปในการสร้างบริการ
ในที่สุดเราก็ตกลงไปที่ Tarantool
เราหันมาใช้มันเมื่อเป็นเวอร์ชัน 1.6 เราสนใจเรื่องนี้โดยอาศัยความสัมพันธ์ระหว่างคีย์-ค่าและฟังก์ชันการทำงานของฐานข้อมูลเชิงสัมพันธ์ มีดัชนีรอง ธุรกรรม และช่องว่าง สิ่งเหล่านี้เหมือนกับตาราง แต่ไม่ง่าย คุณสามารถจัดเก็บคอลัมน์จำนวนต่างๆ ในนั้นได้ แต่คุณสมบัติที่โดดเด่นของ Tarantool คือดัชนีรองที่รวมกับคีย์-ค่าและการทำธุรกรรม
ชุมชนที่พูดภาษารัสเซียที่ตอบสนองและพร้อมที่จะช่วยเหลือในการแชทก็มีบทบาทเช่นกัน เราใช้สิ่งนี้อย่างจริงจังและอยู่ในแชทโดยตรง และอย่าลืมการยืนหยัดอย่างเหมาะสมโดยไม่มีข้อผิดพลาดและข้อผิดพลาดที่ชัดเจน หากคุณดูประวัติของเรากับ Tarantool เรามีความเจ็บปวดและความล้มเหลวมากมายกับการจำลองแบบ แต่เราไม่เคยสูญเสียข้อมูลเนื่องจากความผิดพลาดของมัน!
การนำไปปฏิบัติเริ่มต้นอย่างคร่าวๆ
ในเวลานั้น สแต็กการพัฒนาหลักของเราคือ .NET ซึ่งไม่มีตัวเชื่อมต่อสำหรับ Tarantool เราเริ่มทำอะไรบางอย่างใน Go ทันที มันทำงานได้ดีกับ Lua เช่นกัน ปัญหาหลักในขณะนั้นคือการดีบั๊ก: ใน .NET ทุกอย่างยอดเยี่ยมในเรื่องนี้ แต่หลังจากนั้นก็ยากที่จะกระโดดเข้าสู่โลกของ Lua ที่ฝังอยู่เมื่อคุณไม่มีการดีบั๊กยกเว้นบันทึก นอกจากนี้ ด้วยเหตุผลบางประการ การจำลองแบบจึงพังทลายลงเป็นระยะๆ ดังนั้นฉันจึงต้องเจาะลึกโครงสร้างของเครื่องยนต์ Tarantool การแชทช่วยในเรื่องนี้และในขอบเขตที่น้อยกว่าในเรื่องเอกสารประกอบ บางครั้งเราก็ดูที่โค้ด ตอนนั้นเอกสารก็พอใช้ได้
ดังนั้น ตลอดหลายเดือนมานี้ ฉันสามารถควบคุมตัวเองได้และได้รับผลลัพธ์ที่ดีจากการทำงานร่วมกับ Tarantool เราได้รวบรวมการพัฒนาอ้างอิงใน git ซึ่งช่วยในการสร้างไมโครเซอร์วิสใหม่ๆ ตัวอย่างเช่น เมื่อมีงานเกิดขึ้น: เพื่อสร้างไมโครเซอร์วิสอื่น นักพัฒนาดูซอร์สโค้ดของโซลูชันอ้างอิงในพื้นที่เก็บข้อมูล และใช้เวลาไม่เกินหนึ่งสัปดาห์ในการสร้างใหม่
นี่เป็นช่วงเวลาพิเศษ ตามอัตภาพ คุณสามารถไปหาผู้ดูแลระบบที่โต๊ะถัดไปแล้วถามว่า: “ขอเครื่องเสมือนให้ฉันหน่อย” ผ่านไปประมาณสามสิบนาที รถก็อยู่กับคุณแล้ว คุณเชื่อมต่อตัวเอง ติดตั้งทุกอย่าง และส่งการรับส่งข้อมูลถึงคุณ
ทุกวันนี้สิ่งนี้จะไม่ทำงานอีกต่อไป: คุณต้องเพิ่มการตรวจสอบและการบันทึกลงในบริการ ครอบคลุมฟังก์ชันการทำงานด้วยการทดสอบ สั่งซื้อเครื่องเสมือนหรือการจัดส่งไปยัง Kuber ฯลฯ โดยทั่วไปวิธีนี้จะดีกว่าแม้ว่าจะใช้เวลานานและยุ่งยากกว่าก็ตาม
แบ่งแยกและปกครอง เกี่ยวอะไรกับลัวะ?
เกิดภาวะที่กลืนไม่เข้าคายไม่ออกร้ายแรง: บางทีมไม่สามารถเปิดตัวการเปลี่ยนแปลงบริการที่มีเหตุผลมากมายใน Lua ได้อย่างน่าเชื่อถือ ซึ่งมักมาพร้อมกับบริการที่ไม่ทำงาน
นั่นคือนักพัฒนากำลังเตรียมการเปลี่ยนแปลงบางอย่าง Tarantool เริ่มทำการโยกย้าย แต่แบบจำลองยังคงอยู่กับโค้ดเก่า DDL บางส่วนหรืออย่างอื่นมาถึงที่นั่นผ่านการจำลองแบบ และโค้ดก็แยกออกจากกันเนื่องจากไม่ได้นำมาพิจารณา เป็นผลให้ขั้นตอนการอัปเดตสำหรับผู้ดูแลระบบถูกวางไว้บนแผ่น A4: หยุดการจำลองแบบ อัปเดตสิ่งนี้ เปิดการจำลองแบบ ปิดที่นี่ อัปเดตที่นั่น ฝันร้าย!
ด้วยเหตุนี้เราจึงมักพยายามไม่ทำอะไรเลยในลัวะ เพียงใช้ iproto (โปรโตคอลไบนารี่สำหรับการโต้ตอบกับเซิร์ฟเวอร์) เท่านี้ก็เรียบร้อย บางทีนี่อาจเป็นการขาดความรู้ในหมู่นักพัฒนา แต่จากมุมมองนี้ระบบมีความซับซ้อน
เราไม่ได้สุ่มสี่สุ่มห้าติดตามสคริปต์นี้ ปัจจุบันเราไม่มีขาวดำ: ทุกอย่างอยู่ใน Lua หรือทุกอย่างอยู่ใน Go เราเข้าใจแล้วว่าจะรวมเข้าด้วยกันได้อย่างไร เพื่อจะได้ไม่เกิดปัญหาการย้ายถิ่นในภายหลัง
ตอนนี้ทารันทูลอยู่ที่ไหน?
Tarantool ใช้บริการในการคำนวณต้นทุนสุดท้ายของสินค้าโดยคำนึงถึงคูปองส่วนลดหรือที่เรียกว่า "โปรโมเตอร์" ดังที่ได้กล่าวไว้ก่อนหน้านี้ ตอนนี้เขากำลังจะเกษียณ: เขาถูกแทนที่ด้วยบริการแคตตาล็อกใหม่ที่มีราคาที่คำนวณไว้ล่วงหน้า แต่เมื่อหกเดือนที่แล้ว การคำนวณทั้งหมดเกิดขึ้นใน Promotizer ก่อนหน้านี้ ครึ่งหนึ่งของตรรกะเขียนด้วยภาษา Lua เมื่อสองปีที่แล้ว บริการนี้กลายเป็นสถานที่จัดเก็บ และตรรกะก็ถูกเขียนใหม่ใน Go เนื่องจากกลไกของส่วนลดเปลี่ยนไปเล็กน้อยและบริการขาดประสิทธิภาพ
หนึ่งในบริการที่สำคัญที่สุดคือโปรไฟล์ผู้ใช้ นั่นคือผู้ใช้ Wildberries ทั้งหมดจะถูกจัดเก็บไว้ใน Tarantool และมีอยู่ประมาณ 50 ล้านคน ระบบที่แบ่งส่วนตาม ID ผู้ใช้ กระจายไปยัง DC หลายแห่งที่เชื่อมต่อกับบริการ Go
จากข้อมูลของ RPS โปรโมเตอร์เคยเป็นผู้นำ โดยมีคำขอถึง 6 คำขอ มีอยู่ช่วงหนึ่งเรามี 50-60 เล่ม ขณะนี้ผู้นำใน RPS คือโปรไฟล์ผู้ใช้ประมาณ 12 บริการนี้ใช้การแบ่งส่วนที่กำหนดเองหารด้วยช่วงของรหัสผู้ใช้ บริการให้บริการมากกว่า 20 เครื่อง แต่มากเกินไป เราวางแผนที่จะลดทรัพยากรที่จัดสรรเนื่องจากความจุ 4-5 เครื่องก็เพียงพอแล้ว
บริการเซสชันเป็นบริการแรกของเราบน vshard และตลับหมึก การตั้งค่า vshard และการอัปเดตตลับหมึกต้องใช้ความพยายามจากเราบ้าง แต่สุดท้ายทุกอย่างก็ผ่านไปด้วยดี
บริการสำหรับการแสดงแบนเนอร์ต่างๆ บนเว็บไซต์และในแอปพลิเคชันมือถือเป็นหนึ่งในบริการแรกๆ ที่เปิดตัวโดยตรงบน Tarantool บริการนี้มีความโดดเด่นเนื่องจากมีอายุ 6-7 ปี แต่ยังใช้งานได้และไม่เคยรีบูทเลย มีการใช้การจำลองแบบ Master-Master ไม่มีอะไรเคยพัง
มีตัวอย่างการใช้ Tarantool สำหรับฟังก์ชันการอ้างอิงอย่างรวดเร็วในระบบคลังสินค้าเพื่อตรวจสอบข้อมูลซ้ำอย่างรวดเร็วในบางกรณี เราพยายามใช้ Redis สำหรับสิ่งนี้ แต่ข้อมูลในหน่วยความจำใช้พื้นที่มากกว่า Tarantool
บริการรายการรอ การสมัครสมาชิกของลูกค้า เรื่องราวที่ทันสมัยในปัจจุบัน และสินค้ารอตัดบัญชียังทำงานร่วมกับ Tarantool ได้อีกด้วย บริการสุดท้ายในหน่วยความจำใช้เวลาประมาณ 120 GB นี่คือบริการที่ครอบคลุมที่สุดข้างต้น
ข้อสรุป
ด้วยดัชนีรองที่รวมกับคีย์-ค่าและการทำธุรกรรม ทำให้ Tarantool เหมาะอย่างยิ่งสำหรับสถาปัตยกรรมที่ใช้ไมโครเซอร์วิส อย่างไรก็ตาม เราประสบปัญหาเมื่อเปิดตัวการเปลี่ยนแปลงบริการที่มีตรรกะมากมายใน Lua ซึ่งบริการต่างๆ มักจะหยุดทำงาน เราไม่สามารถเอาชนะสิ่งนี้ได้ และเมื่อเวลาผ่านไป เราก็พบการผสมผสานระหว่าง Lua และ Go ที่แตกต่างกัน เรารู้ว่าควรใช้ภาษาหนึ่งที่ไหนและอีกภาษาหนึ่งใช้ที่ไหน
มีอะไรอีกให้อ่านในหัวข้อ
- การสร้างแอปพลิเคชั่นที่มีโหลดสูงตั้งแต่เริ่มต้นโดยใช้ Tarantool
habr.com/ru/company/mailru/blog/510440 - ตัวเลือกผู้นำที่เชื่อถือได้ในตลับ Tarantool
habr.com/ru/company/mailru/blog/513912 - ช่องโทรเลข Tarantool พร้อมข่าวสารเกี่ยวกับผลิตภัณฑ์
t.me/tarantool_news - สนทนาเกี่ยวกับ Tarantool ในการแชทของชุมชน
t.me/tarantoolru
ที่มา: will.com