Jira เป็น Turing-complete
(seriot.ch)- Jira Automation สามารถแสดงเครื่อง Minsky ที่มีตัวนับไม่สิ้นสุดสองตัวและสถานะคำสั่งแบบจำกัดได้ จึงใช้แสดงว่า Jira มีความเป็น Turing-complete ได้
- สถานะของ Epic ทำหน้าที่เป็น program counter ส่วนจำนวน Bug และ Task ที่ลิงก์อยู่คือรีจิสเตอร์สองตัว และกฎ Automation คือ dispatch table ตามแต่ละคำสั่ง
INCและDECถูกทำให้เกิดขึ้นผ่านการสร้างและลบ linked issue ส่วนการแตกแขนงแบบมีเงื่อนไขตัดสินโดย กฎเงื่อนไข JQL ที่ตรวจจำนวน linked issue- ตัวอย่างการบวกเริ่มจาก
A=2,B=3แล้วลด Bug และเพิ่ม Task จนไปถึง Task 5 รายการ และสถานะหยุดบนการรันจริงที่*.atlassian.net - ตัวอย่าง Fibonacci ลดจำนวนลูปด้วยการแปลงประเภท issue และเมื่อชนกับ ขีดจำกัด chain depth 10 ของ Jira Cloud ก็สามารถสั่ง trigger ใหม่ด้วยตนเองเพื่อให้ทำงานต่อได้
เครื่อง Minsky ที่สร้างด้วย Jira Automation
- Jira Automation สามารถแสดง Minsky register machine ที่มีตัวนับไม่สิ้นสุดสองตัวและสถานะคำสั่งแบบจำกัดได้ จึงสามารถทำ reduction เพื่อแสดงว่า Jira เป็น Turing-complete
- โมเดลที่ Minsky พิสูจน์ไว้ในปี 1967 ประกอบด้วยเพียง
INC r; goto Sและif r == 0 goto S else (DEC r; goto S') - องค์ประกอบของ Jira จับคู่ได้กับแต่ละองค์ประกอบของเครื่อง Minsky
- Register A: จำนวน issue ประเภท Bug ที่ลิงก์อยู่
- Register B: จำนวน issue ประเภท Task ที่ลิงก์อยู่
- Program Counter: สถานะของ issue ประเภท Epic เพียงตัวเดียว
- Dispatch Table: กฎ Jira Automation สำหรับแต่ละสถานะคำสั่ง
- Clock: การเปลี่ยนสถานะที่ automation เป็นตัว trigger หรือการ trigger ใหม่จากภายนอกหลังชน chain limit
- สถานะของ Epic เข้ารหัสคำสั่งปัจจุบัน และ Automation rules จะตรวจจำนวน linked issue เพื่อเปลี่ยนไปยังสถานะถัดไป
INCและDECถูกทำให้เกิดขึ้นผ่านการสร้างและการลบ linked issue ตามประเภทที่เกี่ยวข้อง ส่วนการแตกแขนงแบบมีเงื่อนไขจัดการด้วย JQL condition rules
ตัวอย่างการทำงานและข้อจำกัด
- โปรแกรมบวก สร้างเป็นโปรแกรม Minsky ที่ลด
Aลงพร้อมเพิ่มBขึ้น1. if A == 0 goto 3 else (DEC A; goto 2) 2. INC B; goto 1 3. HALT - การติดตั้งใช้งานขั้นต่ำประกอบด้วย Epic หนึ่งตัว, linked issue 5 รายการ, และกฎ Automation อย่างละหนึ่งข้อสำหรับแต่ละสถานะคำสั่ง
-
เวิร์กโฟลว์และกฎ
- ใน Jira Workflow ให้สร้างสถานะเริ่มต้น
BACKLOGจากนั้นมีTODO,DEV,PRODและตั้งให้ทุกสถานะเปลี่ยนถึงกันได้ทั้งหมด - สร้าง Epic หนึ่งตัวในสถานะ
BACKLOG - กฎ
TODOใช้แทนDEC A; if A=0 halt, else goto DEV- จะถูก trigger เมื่อสถานะ Epic เปลี่ยนเป็น
TODO - ถ้ามี Bug ที่ลิงก์อยู่อย่างน้อยหนึ่งรายการ ให้ลบ Bug หนึ่งรายการแล้วเปลี่ยน Epic ไปเป็น
DEV - ถ้าไม่มี Bug ให้เปลี่ยน Epic ไปเป็น
PRODเพื่อหยุดการทำงาน
- จะถูก trigger เมื่อสถานะ Epic เปลี่ยนเป็น
- กฎ
DEVใช้แทนINC B; goto TODO- จะถูก trigger เมื่อสถานะ Epic เปลี่ยนเป็น
DEV - สร้าง Task ใหม่แล้วลิงก์เข้ากับ Epic
- เปลี่ยน Epic ไปเป็น
TODO
- จะถูก trigger เมื่อสถานะ Epic เปลี่ยนเป็น
- ทั้งสองกฎเปิดใช้งาน "Allow rule to trigger other rules"
- ใน Jira Workflow ให้สร้างสถานะเริ่มต้น
-
ค่าเริ่มต้นและผลลัพธ์
- ลิงก์ Bug 2 รายการและ Task 3 รายการเข้ากับ Epic เพื่อกำหนดค่าเริ่มต้นเป็น
A=2,B=3 - เมื่อเปลี่ยน Epic ไปเป็น
TODOจะเกิดการทำงานต่อเนื่องตามลำดับนี้(2,3) TODO → (1,3) DEV → (1,4) TODO → (0,4) DEV → (0,5) TODO → (0,5) PROD - มีการรันบนอินสแตนซ์
*.atlassian.netจริง และท้ายที่สุด Epic ไปถึงPRODพร้อม Bug 0 รายการและ Task 5 รายการที่ลิงก์อยู่ - การรันนี้จึงเป็นผลลัพธ์ของการคำนวณ
2 + 3 = 5ด้วย Jira Automation
- ลิงก์ Bug 2 รายการและ Task 3 รายการเข้ากับ Epic เพื่อกำหนดค่าเริ่มต้นเป็น
-
โครงแบบ Fibonacci
- Convert Issue Type สามารถเปลี่ยนประเภท issue ได้ทันที เช่น Bug → Story, Story → Task
CONVERTสามารถเขียนแทนได้ด้วยDEC + INCจึงไม่ได้เพิ่มความสามารถในการคำนวณ แต่ช่วยลด dispatch table ของลูปการย้ายค่า ทำให้จัดการโปรแกรมที่ซับซ้อนขึ้นได้ง่ายกว่า- Fibonacci เขียนได้เป็น
(A, B) → (B, A+B)และประกอบด้วยรีจิสเตอร์สามตัวA=Bug,B=Task,C=StoryกับสามสถานะTODO,QA,DEVTODO: if any linked Task exists: CONVERT Task → Story INC Bug transition to TODO else: transition to QA QA: if any linked Bug exists: CONVERT Bug → Task transition to QA else: transition to DEV DEV: if any linked Story exists: CONVERT Story → Bug transition to DEV else: transition to TODO - สถานะเริ่มต้นคือ
A=1,B=1,C=0และลำดับ1, 1, 2, 3, 5, 8, 13, ...จะปรากฏในBซึ่งก็คือ จำนวน Task - ต่างจากเครื่องบวก เครื่อง Fibonacci ไม่มีสถานะหยุด และจะทำงานไปจนถึงขีดจำกัด chain depth 10 ของ Jira Cloud
- เมื่อชนข้อจำกัดนี้ ผู้ดูแลสามารถ trigger Epic ใหม่เพื่อให้ทำงานต่อได้ และการแก้ไขสถานะเพียงครั้งเดียวก็จะเริ่มการทำงานแบบลูกโซ่อีกครั้ง
- Jira Data Center เปิดเผย
automation.rule.execution.timeoutและการตั้งค่าที่เกี่ยวข้องในรูปของคุณสมบัติที่กำหนดค่าได้ - หากสมมติให้มีการสร้าง issue และการรันกฎได้ไม่สิ้นสุด ภาษา automation ของ Jira ก็สามารถเข้ารหัสเครื่องแบบสองตัวนับได้ และตามเกณฑ์ทั่วไปที่ถือว่าคอมพิวเตอร์จริงก็มีขีดจำกัดเช่นกัน โควตาแบบจำกัดของ Jira Cloud จึงไม่ได้หักล้างโครงสร้างนี้
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
Jira แย่มากเสียจนมันมีศักยภาพจะกลายเป็นความแย่ในรูปแบบอื่นได้ทุกแบบ
ถ้า Marx รู้จัก Atlassian,
Grundrisseคงลุกเป็นไฟอย่างหนักดูได้จากการเทียบ GitHub Issues ในปี 2014 กับตอนนี้: https://github.com/features/issues
และก็ยังคงเพิ่มฟีเจอร์ซ้ำซ้อนต่อไปเรื่อย ๆ
Jira ได้รับความนิยมมาก และก็มี API wrapper สำหรับภาษาที่คุณชอบค่อนข้างพร้อม
น่าแปลกที่นักพัฒนาองค์กรสายแฮ็กเกอร์ยังไม่ได้ทำให้สิ่งที่ Jira บังคับให้ทำส่วนใหญ่เป็นอัตโนมัติด้วยอะไรอย่างสคริปต์บรรทัดคำสั่ง Python
ถ้าฉันทำให้มันใช้ง่ายกว่าคนที่ยัดเยียด Jira ให้ฉันได้เกินหนึ่งหลัก เกมก็จะพลิก และ Jira ก็จะกลายเป็นเครื่องมือที่ถูกผลักดันเพื่อปกป้องตัวมันเอง
บางครั้งฉันเคยใช้ Jira แบบเกือบจะด้วยเจตนาร้าย และมันยอดเยี่ยมมากสำหรับการปัดความรับผิดชอบ
ถ้ามีปัญหา ก็แค่พูดว่า “ทั้งหมดนี้เขียนไว้อย่างชัดเจนใน Jira อัปเดตกว่าหลายร้อยรายการที่ฉันเขียนไว้ และคุณก็อ่านอยู่ใช่ไหม?”
ตอนนี้มี AI แล้ว ก็แค่ผูกทุกอย่างเข้าด้วยกันด้วยสคริปต์คัสตอม แล้วให้ AI ทำงานจิปาถะของ Jira แทน
บ่อยครั้ง API ทำสิ่งที่ UI ไม่ยอมให้ทำได้ และเพราะทุกคนขยับตัวตาม UI กันหมด ก็เลยตกลงไปในมุมที่พังแบบประหลาด
ตัวอย่างเช่น คุณอาจไม่รู้ว่า
custom_field_5537ต้องจับคู่กับcustom_field_442ถึงจะไปโผล่บนแดชบอร์ดของคนอื่นแล้ว
custom_field_10995ก็อ้างว่าเป็นฟิลด์จำนวนเต็ม และใน XML ก็ส่งกลับมาเป็นจำนวนเต็ม แต่ตอนสร้างงานกลับต้องใช้สตริงค่าคงที่เวทมนตร์ที่ไม่มีเอกสารกำกับ ส่วนตอนอัปเดตกลับไม่ต้องใช้ในเว็บ UI ก็ไม่ได้ทำแบบนั้น ทั้งใน HTML และคำขอกลับใส่เป็นจำนวนเต็มธรรมดา และ 80% ของสตริงก็ตรงกับข้อความที่แสดงในดรอปดาวน์เท่านั้น
การทำ Jira automation คือประสบการณ์การเขียนโปรแกรมที่แย่ที่สุดที่ฉันเคยเจอ
แน่นอนว่าต้องมีการตั้งค่าที่ง่ายกว่านี้และอาจใช้งานง่ายพอสมควร แต่ถึงอย่างนั้นมันก็เละเทะเกินไป
น่าเศร้าที่ความพยายามที่ลงไปก็ยังคุ้มค่าอย่างสมบูรณ์ แนะนำมาก
เราเพิ่มฟิลด์ข้อความคัสตอมสำหรับคำอธิบายที่มนุษย์อ่านได้ลงในแต่ละ ticket และเมื่อ release ถูก deploy สคริปต์ deploy ก็จะเติม timestamp ให้อัตโนมัติ
เราปล่อย release ทีละหนึ่ง ticket ต่อหนึ่งหน่วยงาน และในหนึ่งวันก็จัดการหลาย ticket
เมื่อรวมกับ custom filter แล้ว Jira ก็ให้ change log ที่มนุษย์อ่านได้สำหรับแต่ละบอร์ดและทั้งบริษัท และข้อความเหล่านี้ก็ถูกส่งไปยัง Slack ให้ฝั่งธุรกิจรู้ว่ามีอะไรถูก deploy
มันยังเป็น release audit log ที่ค้นหาได้และเชื่อมโยงกับการเปลี่ยนแปลงของโค้ดด้วย
กระบวนการ deploy ยังเปลี่ยนสถานะของ Jira ticket ด้วย ดังนั้นนักพัฒนาแค่ merge ticket เข้า
mainมันก็ถูก deploy และเสร็จสมบูรณ์ใน Jira ไปพร้อมกันยังมีสคริปต์เล็ก ๆ จำนวนมากที่สร้าง ticket สำหรับงานที่ทำซ้ำโดยอัตโนมัติ
ตลอดหลายปีมานี้มันเสถียรมาก และก็น่าจะยังทำงานอยู่จนถึงตอนนี้
กฎการตั้งชื่อ custom field ไม่ค่อยดีนัก แต่ถ้าทีมควบคุมการตั้งค่า Jira เอง ก็ไม่ยากที่จะรักษามาตรฐานเดียวกันไว้ทั้งหมด
ตอนแรกฉันไม่ชอบ Jira และเมื่อก่อนมันก็มีปัญหาเยอะ แต่ทุกวันนี้ถ้าตั้งค่าดี ๆ มันก็ไม่ได้แย่ขนาดนั้น
ถ้าเป็นบริษัทของฉันเองฉันคงไม่เลือกมัน แต่จากมุมมองของคนที่ใช้มันทั้งในฐานะนักพัฒนา ผู้ดูแล ผู้ใช้ปลายทาง และนักพัฒนาเครื่องมือภายใน พอมันถูกตั้งค่าและเริ่มทำงานแล้ว ส่วนใหญ่ก็ไม่ได้เกะกะอะไรนัก
ถ้าเพิ่มข้อความเข้าไปอีก somehow Jira ก็คงยิ่งช้าลง เพราะมันจะพยายามรันทุกอย่างอัตโนมัติบนข้อความทั้งหมดนั้นอยู่เสมอ
ถ้าบริษัทต้องการเครื่องทำความร้อน ก็ใช้ Jira ได้เลย
มันยืดหยุ่นพอสมควร และด้วย AI ก็ยิ่งเปิดทางมากขึ้น
กระบวนการส่วนใหญ่พึ่งพา Jira และการเปลี่ยนสถานะบางอย่างจะเรียก webhook สำหรับ automation
ทันทีที่ได้สิทธิ์เข้าถึง AI หนึ่งในสิ่งแรกที่ฉันทำคือสร้าง Jira MCP
ตอนนี้ฉันพยายามแทบไม่แตะ Jira โดยตรงแล้ว และให้ Claude สร้าง Jira issue, เขียนคอมเมนต์, สร้าง subtasks, ลิงก์ issue ฯลฯ
เมื่อก่อนฉันกลัวทุกครั้งที่ต้องไปสำรวจวิธี implement และแยกมันออกเป็นงานย่อย
เพราะยิ่งแยกละเอียดเท่าไร ก็ยิ่งต้องสร้าง Jira issue มารองรับแต่ละงานมากขึ้นเท่านั้น
ตอนนี้ก็แค่จัดทุกอย่างไว้ในไฟล์ แล้วโยนงานจิปาถะของ Jira ให้โมเดลภาษาขนาดใหญ่จัดการ
พอกลับไปที่บริษัทเก่าที่เคยทำ ก็ยังใช้ JIRA กันอยู่
ตอนสัมภาษณ์งานก็แน่นอนว่าตอบไปว่า “อ๋อ JIRA เหรอ ยังใช้อยู่อีกเหรอ? ใช้ได้สิ”
ในทางปฏิบัติก็ใช้งานได้แหละ แต่พอเห็น JIRA เวอร์ชันล่าสุดแล้วช็อกจริง ๆ
มีความขัดใจจุกจิกเป็นพันอย่าง และหนึ่งในอย่างที่แย่ที่สุดคือพอพยายามดับเบิลคลิกเพื่อเลือกข้อความ อยู่ ๆ ฟิลด์ก็เข้าสู่โหมดแก้ไข
ที่ผมจำได้คือ JIRA Server 4.0 และย้อนดูความทรงจำได้ที่นี่: https://www.jirastrategy.com/wp-content/uploads/2020/04/depl...
ถ้าซูมมากพอจะเห็นว่าแต่ละ issue มีชื่อ ประเภท เวอร์ชันที่แก้แล้ว เวอร์ชันที่ได้รับผลกระทบ ฯลฯ และต่อด้วยคอมเมนต์ได้ทันที โครงสร้างมันเรียบง่ายมาก
น่าหงุดหงิดสุด ๆ มันพลาดแม้แต่เรื่องพื้นฐานอย่าง การจัดการข้อความ
แต่มีผู้จัดการโปรเจกต์บางคนบอกว่าชอบแบบนั้น
เพราะเขาไม่ได้ใช้การดับเบิลคลิกแล้วลากเพื่อเลือกทั้งคำ
ก็เหมือนเดิม ผู้ใช้ที่ชำนาญกว่าต้องถูกดึงลงมาเพราะความสะดวกของคนที่แทบใช้คอมพิวเตอร์ไม่เป็น
ทำให้สงสัยว่าผมพลาดอะไรไป หรือหล่นเข้าไปในรูหนอนเวลาหรือเปล่า
ไม่เข้าใจว่าทำไมพูดถึง Jira เหมือนมันเป็น Visicalc
ตอนนี้ผมไม่ได้ทำงานในบริษัท IT แล้ว เลยอาจพลาดความเปลี่ยนแปลงครั้งใหญ่บางอย่างในช่วง 2 ปีที่ผ่านมา
ตอนเปลี่ยนจาก 4.x ไป 6.x ความขัดใจจุกจิกพวกนี้ก็ตามมาด้วย เหมือนกับกล่องโยกเยกของ OFBiz และผลิตภัณฑ์คนละตัวที่แค่ทำให้หน้าตาดูคล้ายกัน
วิศวกรพวกนั้นออกไปนานแล้ว และเอา 280 ดอลลาร์ต่อหุ้น ติดมือไปด้วย
ปัญหาหลักของ JIRA คือสิทธิ์ในการตั้งค่าให้ถูกต้องมักอยู่ในมือคนไม่กี่คนเสมอ และคนพวกนั้นก็มักจะขี้เกียจ ไม่มีเวลา หรือไม่ได้ใช้งานทุกวันเลยไม่ใส่ใจ
แน่นอนว่าปัญหาไม่ได้มีแค่นั้น
มันช้าจนนึกภาพแทบไม่ออก แถมยังมีข้อจำกัดประหลาดอย่าง issue หนึ่งเป็น parent ของอีก issue หนึ่งไม่ได้
JIRA ช้าเกินไป และดูเหมือนผู้ดูแลระบบจะไม่รู้วิธีตั้งค่าให้ถูกต้อง
แค่เคยใช้ก็มี บาดแผลทางใจ แล้ว
ก็แค่บนโลกนี้ไม่มีคนสักคนที่ตั้งค่าเครื่องมือนี้ให้ถูกต้องได้
จะไปโทษเครื่องมือเพราะความไร้ความสามารถของมนุษย์ก็คงไม่ได้
ส่วนคำถามว่าถ้างั้นมันถูกสร้างมาเพื่อใคร นั่นเป็นอีกประเด็นหนึ่ง และตอนนี้ไม่ควรคุยกัน
โดยแก่นแท้แล้ว ระบบตั๋วก็ไม่ได้เป็นอะไรมากไปกว่าฐานข้อมูลที่เก็บตั๋ว ความสัมพันธ์ระหว่างตั๋ว และสถานะ
แน่นอนว่าถ้ามีตั๋วที่เชื่อมกันเยอะ custom field เยอะ และปลั๊กอินเยอะ ความซับซ้อนก็อาจระเบิดได้
ถึงอย่างนั้นก็ยังไม่เข้าใจว่าของที่จัดการแค่ข้อมูลข้อความธรรมดากับไฟล์แนบ จะช้าจนทนไม่ได้ขนาดนั้นได้ยังไง
ในที่สุดก็เล่น Doom บน Jira ได้แล้ว
https://mattrickard.com/accidentally-turing-complete
แบบนี้ก็อธิบายได้ว่าทำไมถึงตัดสินไม่ได้ว่างาน Jira บางงานจะ หยุดทำงานหรือไม่
Jira มี ปืนลูกซองปั๊มแอ็กชัน สำหรับฆ่าปีศาจที่เกิดจากผลลัพธ์นั้นด้วยหรือเปล่า?
ลองใช้ Azure Boards แทนแล้วคุณจะรัก JIRA เอง
เพราะไม่มีซอฟต์แวร์อะไรในโลกที่ Microsoft ทำให้แย่ลงกว่าเดิมไม่ได้
หาคำมาบรรยายความดูแคลนที่มีต่อ Outlook ได้ยากมาก และคำว่า “ไม่ชอบ” ยังห่างไกลจากความจริงมาก
แม้แต่งานพื้นฐานที่สุดอย่างการรับและส่งอีเมลมันยังทำได้อย่างทุลักทุเล
มีแจ้งเตือนอีเมลเข้าบนมือถือ พอเปิดแอปกลับไม่มีอะไรเลย และต่อให้ดึงลงมารีเฟรชก็ไม่เกิดอะไรขึ้น
ปกติต้องรออีก 1-15 นาทีถึงจะโผล่มา
ทุกอย่างที่ทำใน Outlook ช่างยุ่งยากอย่างร้ายกาจ
ผมเคยใช้ Outlook ตั้งแต่สมัย Office 2003 แต่จำไม่ได้ว่ามันเคยแย่ขนาดนี้ ไม่รู้ว่ามันถอยหลังลงคลองมาได้ยังไง
ยังไม่อยากเริ่มพูดถึง Teams เลย ผมยังไม่ค่อยเข้าใจด้วยซ้ำว่าโปรแกรมนั้นพยายามแก้ปัญหาอะไร
ไฟล์ที่แชร์อยู่ใน OneDrive แต่ก็อยู่ใน Teams ด้วย และด้วยเหตุผลบางอย่างก็ไปอยู่ใน Outlook ด้วย
ผมต้องย้ายไฟล์แบ็กอัปคอมพิวเตอร์, อิมเมจ CloneZilla ราว 30GB ไปไว้ใน OneDrive/Teams/Outlook ซึ่งใช้เวลาชั่วกัปชั่วกัลป์ และพัดลมโน้ตบุ๊ก Ryzen 6 คอร์ที่ลง Win11 ก็หมุนอย่างบ้าคลั่งตลอดเวลา
ได้ยังไง? ทำไม?
workflow และ orchestration engine ทุกตัวล้วนเป็น Turing-complete
เพราะจุดประสงค์ของมันก็คือทำให้ flow การทำงานเป็นอัตโนมัติ
หรือให้คนมากด trigger ใหม่แล้วทำต่อจากจุดที่ค้างไว้ได้?
อย่างแรก JIRA ไม่ใช่ orchestration
อย่างที่สอง สิ่งที่ workflow ควรทำคือเชื่อมสถานะบางอย่างเข้ากับข้อมูลภายนอก และทำให้จัดการมันได้ง่าย
ถ้าจะเป็น Turing-complete คุณต้องมี trigger กับ rule, อะไรสักอย่างอย่างตัวนับที่เพิ่มได้ไม่สิ้นสุด, stack สองอัน, หรือเทปสองทิศทางอะไรทำนองนั้น
ลองพิสูจน์ว่าผมผิดสิ
ผมชอบระบบ automation ของ Jira
เวลาเข้าทีมใหม่ที่ใช้ Jira ผมจะตั้ง automation ให้รวม story point ของ subtasks ไปใส่ที่ story point ของงานแม่อัตโนมัติ หรือถ้าตั๋วยังไม่มีคุณสมบัติที่ผ่านการขัดเกลามากพอ ก็ย้ายกลับไปที่ backlog อัตโนมัติ
เช่น กรณีที่ไม่มี assignee, story point, priority หรือ description
แค่ผ่านไปหนึ่งสปรินต์ บอร์ดของทีมก็เป็นระเบียบขึ้นมาก
ไม่รู้ว่าทำไมถึงไม่ตั้งมาเป็นค่าเริ่มต้น แต่แก้ได้ง่ายด้วย automation
assignee ควรเป็นคนที่รับงานนั้นไปทำแล้วค่อยถูกกำหนด ไม่ควรเป็นส่วนหนึ่งของขั้นตอนการขัดเกลา