EST. 2026 — บันทึกงานเซิร์ฟเวอร์จริง SYSADMIN · SECURITY · AI · DIRECTADMIN เล่มที่ 01
THE SYSADMIN ALMANAC
หน้าหลัก / Linux / ฟีดข่าวค้างสามวันทั้งที่งานรันครบ เพราะฟีดไม่ได้เรียงตามวันที่
Linux

ฟีดข่าวค้างสามวันทั้งที่งานรันครบ เพราะฟีดไม่ได้เรียงตามวันที่

โดย Ton-Or ·25 ก.ย. 2026·อ่าน 10 นาที
ฟีดข่าวค้างสามวันทั้งที่งานรันครบ เพราะฟีดไม่ได้เรียงตามวันที่

งานที่ล้มแล้วขึ้นแดงยังพอไล่ได้ แต่งานที่รันครบ คืนค่าศูนย์ และรายงานว่าสำเร็จทุกชั้น ทั้งที่ผลลัพธ์นิ่งสนิทมาสามวัน อันนี้กว่าจะรู้ตัวคือมีคนมาทักว่าเว็บไม่มีข่าวใหม่

พอดีผมดูแลเว็บข่าวเฉพาะทางตัวหนึ่ง ที่ตามข่าวของแบรนด์ในอุตสาหกรรมเดียวกันประมาณยี่สิบกว่าราย แล้วเอามาขึ้นเป็นข่าวเด่นหน้าแรกทุกเช้า วันหนึ่งมีคนทักมาว่า "ไม่มีข่าวทุกวันเหรอ" ผมเปิดดูก็เจอว่าข่าวเด่นค้างอยู่ที่วันที่เดิมมาสามวันแล้ว ที่ทำเอาผมงงคือไปไล่ log ทุกขั้นตอน งานตามเวลารันครบทุกวัน คิวแปลทำครบ ตัวเผยแพร่พิมพ์ว่า RESULT OK ทุกครั้ง exit 0 หมด ไม่มี error สักบรรทัดเดียวในระบบทั้งเส้น สุดท้ายเป็นสามอย่างซ้อนกัน เลยเก็บมาเขียนไว้

ชื่อเว็บ ชื่อแบรนด์ ชื่อคำค้น และคีย์ทั้งหมดในบทความเป็นตัวอย่างที่ผมสมมติขึ้นแทนของจริง ส่วนโครงสร้าง ตัวเลขที่วัดได้ และโค้ดคือของจริงจากงานนั้น

เส้นทางของข่าวหนึ่งชิ้นตัวรวมข่าวยิงคำค้นทีละแบรนด์->ด่านกรองพาดหัวต้องมีชื่อแบรนด์->ไฟล์สะสมกันซ้ำด้วย url->ขั้นแปลทำเฉพาะรายการใหม่->ตัวเผยแพร่ยิงเข้า API เว็บ

สิ่งที่ทำให้เรื่องนี้ยืดยาวคือ ทุกชั้นในเส้นทางข้างบน ทำงานถูกตามที่มันถูกสั่งให้ทำจริงๆ ตัวรวมข่าวยิงคำค้นครบ ได้ผลกลับมา เขียนไฟล์สำเร็จ ขั้นแปลอ่านไฟล์แล้วไม่เจอรายการใหม่ก็เขียนกลับเท่าเดิม ตัวเผยแพร่อ่านรายการเดิม ยิงเข้า API สำเร็จ คืน exit 0

พูดง่ายๆ คือ ทุกชั้นตรวจว่าตัวเองทำงานเสร็จ ไม่มีชั้นไหนตรวจว่าผลลัพธ์เปลี่ยนไปจากเมื่อวานหรือเปล่า ผมเจอรูปแบบนี้ซ้ำหลายรอบในระบบอัตโนมัติ และทุกรอบมันเงียบเหมือนกันหมด

exit 0 ไม่ได้แปลว่างานได้ผล

สคริปต์คืนค่าศูนย์แปลว่า "โค้ดวิ่งจนจบโดยไม่มีข้อยกเว้น" เท่านั้น มันไม่ได้แปลว่ามีของใหม่เข้าระบบ ระบบที่ผลลัพธ์เป็นสตรีมของข้อมูลสด ต้องมีการยืนยันคุณสมบัติของผลลัพธ์ต่างหาก ไม่ใช่ยืนยันแค่ว่าโปรแกรมรันจบ

กับดักที่หนึ่ง: RSS เรียงตามความเกี่ยวข้อง ไม่ใช่วันที่

ตัวรวมข่าวรุ่นแรกผมเขียนด้วย Node ดึง RSS ของบริการรวมข่าวมาแล้วตัดเอา 5 อันแรก ซึ่งดูสมเหตุสมผลมากถ้าคิดว่าฟีดเรียงใหม่สุดมาก่อน

// ก่อนแก้ — ตัดจากบนสุดตรงๆ โดยเดาว่าฟีดเรียงตามวันที่ให้แล้ว
function parseItems(xml, limit) {
  return [...xml.matchAll(/<item>([\s\S]*?)<\/item>/g)]
    .slice(0, limit)                       // <-- ต้นเหตุอยู่บรรทัดนี้
    .map(m => readOne(m[1]));
}

ปัญหาคือฟีดตัวนั้น เรียงตามความเกี่ยวข้อง ไม่ใช่วันที่ ผมพิมพ์ลำดับที่ได้จริงออกมาดูวันหนึ่ง มันเรียงมาเป็น 2 ก.ย. แล้ว 6 ก.ย. แล้ว 3 ก.ย. คือสลับกันไปมา ข่าวของวันนี้มีอยู่จริงในฟีดเดียวกันนั่นแหละ แต่มันไปนอนอยู่ลำดับล่างๆ พอเราตัดเอาแค่ห้าอันแรก ก็ได้ชุดเดิมทุกวัน แล้วขั้นถัดไปที่กันซ้ำด้วย url ก็ข้ามทั้งหมด เพราะ ทุกลิงก์ที่ได้มาคือลิงก์ที่เคยเห็นแล้ว ฟีดหน้าเว็บเลยไม่ขยับ

ทางแก้คือแยกสองเรื่องออกจากกันให้ชัด: อ่านมาทั้งฟีดก่อน แล้วค่อยเรียงเอง แล้วค่อยตัด

// หลังแก้ — อ่านทุก item, เรียงตาม pubDate ที่แปลงเป็นเวลาจริง, แล้วค่อยตัด
function parseItems(xml, limit, must) {
  const items = [...xml.matchAll(/<item>([\s\S]*?)<\/item>/g)].map(m => {
    const b = m[1];
    const get = re => { const x = b.match(re); return x ? decode(x[1]) : ''; };
    const rawTitle = get(/<title>([\s\S]*?)<\/title>/);
    const source   = get(/<source[^>]*>([\s\S]*?)<\/source>/);
    // พาดหัวของบริการนี้ลงท้ายด้วย " - <สำนักข่าว>" เสมอ ตัดทิ้งให้เหลือหัวข้อสะอาด
    const title = source && rawTitle.endsWith(' - ' + source)
      ? rawTitle.slice(0, -(source.length + 3)) : rawTitle;
    return { title, source, link: get(/<link>([\s\S]*?)<\/link>/),
             pubDate: get(/<pubDate>([\s\S]*?)<\/pubDate>/) };
  });
  // แปลงเป็น epoch ก่อนเทียบ ถ้าแปลงไม่ได้ให้เป็น 0 (ตกไปท้ายแถว ไม่ใช่ขึ้นหัวแถว)
  const ts = it => { const t = Date.parse(it.pubDate); return Number.isFinite(t) ? t : 0; };
  return items.filter(hit(must)).sort((a, b) => ts(b) - ts(a)).slice(0, limit);
}

อีกตัวที่ผมปรับพร้อมกันคือหน้าต่างเวลาในคำค้น ของเดิมขอย้อนหลัง 45 วัน ซึ่งพอรวมกับการเรียงตามความเกี่ยวข้องแล้วมันคือสูตรของการได้ข่าวเก่าซ้ำๆ ผมลดเหลือ 10 วัน กองผู้สมัครเลยสดขึ้นชัดเจน

กับดักที่สอง: เรียงวันที่แบบสตริง เท่ากับเรียงตามชื่อวันในสัปดาห์

ตัวถัดไปในเส้นทางเขียนด้วย Python และมันก็เรียงวันที่เหมือนกัน แต่เรียงจาก pubDate ดิบที่เป็นรูปแบบ RFC-822 ตรงๆ

# รูปแบบที่ฟีดส่งมา — ขึ้นต้นด้วยชื่อวันในสัปดาห์เสมอ
"Tue, 08 Sep 2026 10:00:00 GMT"
"Fri, 04 Sep 2026 07:30:00 GMT"
"Mon, 07 Sep 2026 22:15:00 GMT"

# เรียงสตริงตรงๆ ได้ลำดับนี้ — เรียงตามตัวอักษรของ "ชื่อวัน" ไม่ใช่วันที่
# Fri < Mon < Sat < Sun < Thu < Tue < Wed
sorted(dates)   # -> Fri 04 ก.ย. มาก่อน Mon 07 ก.ย. มาก่อน Tue 08 ก.ย.

นี่คือเหตุผลที่ข่าวใหม่สุดแพ้ข่าวอายุสัปดาห์หนึ่งได้เรื่อยๆ โดยไม่มีอะไรผิดพลาดให้เห็น ผมเลยเขียนฟังก์ชันดึงตัวเลขออกมาเรียงแทน

MON = {v: i for i, v in enumerate(
    ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"], 1)}

def pubkey(it):
    """คีย์เรียงลำดับจาก pubDate แบบ RFC-822 ("Tue, 14 Jul 2026 10:00:00 GMT")
    การเรียงสตริงดิบคือการเรียงตามชื่อวันในสัปดาห์ (Fri < Mon < Sat < Sun < Thu < Tue < Wed)
    ซึ่งคือสาเหตุที่ข่าวใหม่สุดแพ้ข่าวเก่ามาตลอด"""
    m = re.search(r"(\d{1,2})\s+(\w{3})\s+(\d{4})(?:\s+(\d{2}):(\d{2}))?",
                  it.get("pubDate", "") or "")
    if not m:
        return ""                      # ไม่มีวันที่ -> คีย์ว่าง -> ตกไปท้ายแถว
    return "%s-%02d-%02d %s:%s" % (m.group(3), MON.get(m.group(2), 1), int(m.group(1)),
                                   m.group(4) or "00", m.group(5) or "00")
วันที่ที่มาจากภายนอกต้องแปลงก่อนเทียบเสมอ

ทุกครั้งที่โค้ดเรียงลำดับด้วยฟิลด์วันที่ที่ยังเป็นสตริงจากผู้ให้บริการภายนอก ให้ถือว่าผิดไว้ก่อน รูปแบบที่ขึ้นต้นด้วยชื่อวัน ชื่อเดือน หรือวันที่แบบ วัน/เดือน/ปี เรียงเป็นสตริงแล้วได้ผลที่ดูสมเหตุสมผลพอที่จะไม่มีใครสังเกต ให้แปลงเป็น epoch หรือรูปแบบ ปี-เดือน-วัน ที่เรียงได้จริงก่อนทุกครั้ง

กับดักที่สาม: ขอมาน้อย แล้วด่านกรองตีตกหมดตั้งแต่ยังไม่ทันได้ดู

พอย้ายไปใช้ API ของผู้ให้บริการข่าวอีกเจ้า ผมนึกว่าจะหมดเรื่องแล้ว เพราะเจ้านี้เรียงใหม่สุดมาก่อนจริง แต่ข่าวก็ยังค้างอีก คราวนี้ต้นเหตุคนละแบบ

โค้ดขอมาแบรนด์ละ 4 ข่าว แล้วเอามาผ่าน ด่านกรองความเกี่ยวข้อง ที่บังคับว่าพาดหัวหรือชื่อสำนักข่าวต้องมีชื่อแบรนด์อยู่ ปรากฏว่าสี่อันแรกของแต่ละแบรนด์โดนด่านตีตกเป็นประจำ ของที่ผ่านด่านได้จริงไปกองอยู่ลำดับ 5 ถึง 10 ซึ่งถูกตัดทิ้งตั้งแต่ยังไม่ได้ดู

ผมวัดจริงในวันเดียวกัน ยิงเท่ากันทั้งสองรอบ

ขอมาแบรนด์ละ 4

ผ่านด่าน 2 ข่าว · ใหม่สุดคือ 4 ก.ย.

  • ยิง API 21 ครั้ง
  • สี่อันแรกถูกตีตกเกือบทุกแบรนด์
  • หน้าเว็บนิ่งสนิท
ขอมาแบรนด์ละ 10

ผ่านด่าน 13 ข่าว · ใหม่สุดคือ 6 ก.ย.

  • ยิง API 21 ครั้งเท่าเดิม
  • ของที่ผ่านด่านอยู่ลำดับ 5-10 ได้เข้ามา
  • ต้นทุนต่อวันเท่าเดิมทุกประการ

จุดที่ผมอยากให้เห็นคือ โควตาของบริการพวกนี้คิดเป็นจำนวนครั้งที่ยิง ไม่ใช่จำนวนรายการที่ได้กลับมา เพราะฉะนั้นการขอมาน้อยกว่าเพดานที่เขาให้ ไม่ได้ประหยัดอะไรเลยสักบาท แต่ทำให้ของที่ผ่านด่านหายไปเฉยๆ ผมเลยตั้งค่าปริยายเป็นเพดานที่ผู้ให้บริการยอมให้ แล้วเขียนคอมเมนต์กำกับไว้เลยว่าห้ามลดกลับ

# ขอมาให้เต็มเพดานที่ผู้ให้บริการยอมให้ต่อหนึ่งคำค้น
# ห้ามลดค่านี้ลง: โควตาคิดเป็น "จำนวนครั้งที่ยิง" ไม่ใช่ "จำนวนข่าวที่ได้"
# ลดลงเมื่อไรของที่ผ่านด่านกรอง (ซึ่งมักอยู่ลำดับ 5-10) จะถูกตัดทิ้งตั้งแต่ยังไม่ได้ดู
PER = int(sys.argv[sys.argv.index("--per") + 1]) if "--per" in sys.argv else 10

for key, q, must in BRANDS:
    url = ("https://api.example.com/v4/search?q=%s&lang=en&max=%d&sortby=publishedAt&apikey=%s"
           % (urllib.parse.quote(q), PER, KEY))
    d, err = call(url)
    used += 1
    if err:
        print("  %-20s ERR %s" % (key, err))
        if "429" in err or "403" in err:
            quota_hit = True; break          # ชนโควตาแล้ว หยุดเลย อย่ายิงต่อให้โดนแบน
        time.sleep(1.1); continue
    ...
    time.sleep(1.1)                          # เพดาน 1 ครั้งต่อวินาที เว้นจังหวะเอง

ด่านกรอง: จำเป็น แต่ต้องเลือกคำให้เป็น

ตอนผมขยายคำค้นให้กว้างขึ้นเพื่อหาข่าวเพิ่ม สิ่งที่ตามมาทันทีในวันเดียวกันคือ ข่าวรีวิวรถยนต์ ข่าวเกษตรจังหวัด และข่าวประชาสัมพันธ์ของบริษัทที่ไม่เกี่ยวอะไรเลย ขึ้นไปนอนอยู่บนเว็บข่าวเฉพาะทาง เพราะเครื่องมือค้นภาษาไทยมันจับคู่หลวมมาก คำค้นที่เอาชื่อแบรนด์มาต่อกับคำสามัญประจำอุตสาหกรรมก็แมตช์ได้เกือบทุกอย่าง

ด่านกรองของผมเลยบังคับว่า พาดหัวหรือชื่อสำนักข่าวต้องมีชื่อแบรนด์อยู่จริง และมีกับดักซ้อนอยู่ในด่านนี้อีกสองข้อ

def norm(s):
    # พาดหัวไทยเว้นวรรคชื่อแบรนด์ไม่เหมือนกัน ("สตาร์ไลน์" กับ "สตาร์ ไลน์")
    # ถ้าไม่ตัดช่องว่างก่อนเทียบ ข่าวจริงของแบรนด์จะโดนตีตกทิ้งว่าไม่เกี่ยว
    return "".join((s or "").lower().split())


def relevant(it, must):
    if not must:
        return True                       # ไม่ได้ตั้งคำไว้ -> เก็บ อย่าเงียบๆ ทิ้งประวัติ
    hay = norm((it.get("title") or "") + " " + (it.get("source") or ""))
    return any(norm(t) in hay for t in must)

ข้อแรกคือเรื่องช่องว่าง ตอนผมยังเทียบแบบไม่ตัดช่องว่าง ข่าวจริงของแบรนด์หนึ่งโดนตีตกทิ้งเป็นประจำเพราะพาดหัวเว้นวรรคกลางชื่อ พอเปลี่ยนมาตัดช่องว่างก่อนเทียบ จำนวนที่เก็บได้ขยับจาก 30 เป็น 35 ในรอบเดียว

ข้อสองคือเรื่องการเลือกคำ ห้ามใช้ชื่อประเทศ ชื่อเมือง หรือคำสามัญเป็นคำด่านเด็ดขาด ผมเคยใช้ชื่อประเทศตัวหนึ่ง ผลคือได้ข่าวไว้อาลัยเชื้อพระวงศ์ของประเทศนั้นกับข่าวราคาน้ำมันของอีกประเทศเข้ามาเต็มไปหมด อีกแบรนด์หนึ่งชื่อไปพ้องกับชื่อผ้าชนิดหนึ่ง และตัวย่อสามตัวอักษรนี่แมตช์ได้กับทุกอย่างบนโลก ต้องใช้ชื่อแบรนด์เต็ม ไม่ใช่คำที่อยู่ในชื่อแบรนด์

แบรนด์ที่ได้ศูนย์ ไม่ได้แปลว่าด่านพัง

มีอยู่สี่แบรนด์ที่ผ่านด่านได้ศูนย์ข่าวติดกันหลายวัน ผมไปนั่งไล่ดูของที่ถูกตีตกทีละชิ้น ปรากฏว่าด่านทำถูกแล้ว ข่าวที่ค้นเจอเป็นข่าวรวมกลุ่มอุตสาหกรรมที่ไม่ได้พูดถึงแบรนด์นั้นจริง ถ้าผ่อนด่านลงเพื่อให้ตัวเลขสวย ผลที่ได้คือเอาข่าวภัยธรรมชาติไปแปะชื่อแบรนด์ ซึ่งแย่กว่าการไม่มีข่าวเยอะ

ไฟล์สะสมไปเรื่อยๆ เลยต้องมีตัวไล่ล้างย้อนหลัง

จุดที่คนมักลืมคือ ไฟล์ผลลัพธ์ของผม สะสมไปเรื่อยๆ ไม่เคยลดลง โค้ดมันเขียนไว้ว่า items = list(prev) แล้วเติมของใหม่ต่อท้าย และตัวเผยแพร่ยิงทั้งลิสต์ขึ้นไปแบบแทนที่ทั้งชุด แปลว่าอะไรก็ตามที่ด่านเคยปล่อยผ่านตอนที่ยังไม่มีด่าน มันจะอยู่บนเว็บตลอดกาล เพราะสตรีมข่าวสายนี้ไม่มีขั้นตอนใช้วิจารณญาณคั่นเลยสักขั้น

ผมเลยเขียนตัวไล่ล้างแยกอีกตัว ใช้ตารางคำด่านชุดเดียวกันไปกวาดของที่เก็บไว้แล้ว โดยให้เป็นโหมดดูเฉยๆ เป็นค่าปริยาย ต้องสั่ง --apply เท่านั้นถึงจะเขียนทับ

def main():
    j = json.load(io.open(ENP, encoding="utf-8"))
    items = j.get("items", [])
    keep = [i for i in items if relevant(i, MUST.get(i.get("key", "")))]
    drop = [i for i in items if i not in keep]
    print("ไฟล์สะสม: %d รายการ | เก็บ %d | ทิ้ง %d" % (len(items), len(keep), len(drop)))
    for d in drop[:20]:
        print("  DROP [%s] %s | %s" % (d.get("key"), d.get("date"), (d.get("title") or "")[:64]))
    if "--apply" not in sys.argv:
        print("\n(โหมดดูเฉยๆ — ใส่ --apply ถึงจะเขียน แล้วค่อยสั่งเผยแพร่ซ้ำ)")
        return
    j["items"] = keep
    json.dump(j, io.open(ENP, "w", encoding="utf-8"), ensure_ascii=False, indent=1)
    print("\nเขียนแล้ว %d รายการ ต่อไปสั่งเผยแพร่เฉพาะส่วนข่าวเพื่อ sync ของจริง" % len(keep))

รอบนั้นผมใช้มันไล่เก็บของที่หลุดเข้ามาตอนยังไม่มีด่าน ได้ข่าวนอกเรื่อง 11 ชิ้น กับข่าวที่แมตช์ผิดจากคำด่านที่เป็นชื่อประเทศอีก 3 ชิ้น

ด่านตรวจความสด: ทำให้งานล้มดังๆ แทนที่จะเงียบ

สามข้อข้างบนคือการซ่อมเหตุ แต่ของที่สำคัญกว่าสำหรับผมคือข้อนี้ เพราะอาการ "ค้างแบบเงียบ" เกิดกับผมซ้ำหลายรอบและทุกรอบต้องรอให้มีคนมาทัก ผมเลยเติมด่านสุดท้ายเข้าไปในตัวเผยแพร่ ให้มันดูวันที่ของข่าวใหม่สุดหลังทำงานเสร็จ ถ้าเก่าเกินเส้นที่ตั้งไว้ ให้พิมพ์เหตุผลแล้วคืนค่าไม่เป็นศูนย์

# เกินกี่วันถือว่าฟีดค้าง — ปรับผ่านตัวแปรสภาพแวดล้อมได้โดยไม่ต้องแก้โค้ด
STALE_DAYS = int(os.environ.get("FEED_STALE_DAYS", "2"))


def freshness_gate(items):
    """ตรวจ 'ผลลัพธ์' ไม่ใช่ 'ว่ารันจบ' — คืนค่าที่ไม่ใช่ศูนย์เพื่อให้ตัวตั้งเวลาขึ้นแดง"""
    if not items:
        print("[gate] ไม่มีรายการที่เผยแพร่ได้เลยในหน้าต่างเวลา -> ถือว่าล้ม")
        return 1
    newest = max((i.get("date") or "") for i in items)
    cut = (datetime.date.today() - datetime.timedelta(days=STALE_DAYS)).isoformat()
    if newest < cut:
        print("[gate] ข่าวใหม่สุดคือ %s เก่ากว่าเส้น %s (FEED_STALE_DAYS=%d)"
              % (newest, cut, STALE_DAYS))
        print("[gate] แปลว่าต้นทางหยุดส่งของใหม่ หรือด่านกรองตีตกหมด -> ไปไล่ที่ต้นทาง")
        return 1
    print("[gate] ข่าวใหม่สุด %s ผ่าน" % newest)
    return 0


sys.exit(freshness_gate(out) or report("feed"))

ค่าปริยาย 2 วันคือตัวเลขที่ผมเลือกจากจังหวะของงานจริง งานรันทุกวัน ถ้าสองวันแล้วยังไม่มีของใหม่เข้ามาเลย แปลว่าต้องมีคนไปดูแล้ว ใครที่ต้นทางออกข่าวถี่กว่านี้ก็ลดลงได้ตามใจ แต่อย่าไปตั้งไว้หลวมจนกลายเป็นด่านที่ไม่เคยทำงาน

4 -> 10จำนวนที่ขอต่อคำค้น
2 -> 13ข่าวที่ผ่านด่านในวันเดียวกัน
21ครั้งที่ยิง API เท่าเดิมทั้งสองรอบ
45 -> 10หน้าต่างเวลาในคำค้น (วัน)
2วันที่ถือว่าค้าง แล้วให้งานขึ้นแดง

เติมแหล่งที่สอง แล้วปัญหาเดิมกลับมาทันที

พอทำทั้งหมดข้างบนเสร็จ ยังมีอีกหกแบรนด์ที่หาข่าวภาษาอังกฤษไม่เจอเลย ผมเลยเติมแหล่งที่สองเป็น RSS ภาษาไทย ซึ่งฟรี ไม่ใช้คีย์ ไม่มีโควตา และคืนได้ถึงร้อยรายการต่อคำค้น เอามาคั่นระหว่างตัวรวมข่าวกับตัวเผยแพร่ เติมรายการลงไฟล์เดียวกัน รูปแบบเดียวกัน ขั้นถัดไปไม่ต้องแก้อะไรเลย

# ต่อเข้าลำดับงานประจำวัน คั่นระหว่างตัวรวมข่าวกับตัวเผยแพร่
python feed_api.py --per 10
python feed_rss.py --all --days 10 --per 6
python feed_publish.py --days 21 --max 24

แต่ที่ต้องย้ำคือ แหล่งที่สองนี้คือแหล่งเดียวกับที่มีกับดักข้อแรก คือเรียงตามความเกี่ยวข้อง ผมยืนยันซ้ำด้วยของจริงอีกรอบว่าลำดับที่ได้คือ 2 ก.ย. แล้ว 6 ก.ย. แล้ว 3 ก.ย. เพราะฉะนั้นตัวใหม่นี้ต้องแปลง pubDate เป็นวันเวลาจริงแล้วเรียงเองก่อนตัดเหมือนกัน ไม่งั้นได้ข่าวเก่าชุดเดิมทุกวัน

ผลวันแรกของแหล่งที่สอง เติมเข้ามา 19 ข่าว รวมกับของเดิมอีก 5 เป็นฟีด 24 ข่าว หน้าเว็บได้ข่าวของวันนี้กับเมื่อวานขึ้นชัดเจน ข้อเสียคือ RSS ไม่มีรูปประจำข่าว ตัวเผยแพร่เลยต้องถอยไปใช้รูปสำรองประจำแบรนด์ รอบแรกได้รูปจริง 26 จาก 40 ที่เหลืออีก 14 ใช้รูปสำรอง ซึ่งผมรับได้

ลำดับที่ผมใช้ไล่เวลาเจออาการแบบนี้

  1. เทียบผลลัพธ์กับเมื่อวานก่อน ไม่ใช่ดู log ก่อน ถ้าจำนวนรายการเท่าเดิมเป๊ะ แปลว่าไม่มีของใหม่เข้าระบบ ไม่ใช่ระบบพัง
  2. ไล่ย้อนจากปลายขึ้นต้นทาง ถามทีละชั้นว่า "ชั้นนี้ได้ของใหม่เข้ามากี่ชิ้น" ไม่ใช่ "ชั้นนี้รันสำเร็จไหม"
  3. พอเจอชั้นที่ได้ศูนย์ ให้พิมพ์ของดิบที่ชั้นนั้นรับเข้ามาทั้งหมดออกมาดูด้วยตา โดยเฉพาะลำดับและวันที่
  4. แยกให้ออกว่า "ต้นทางไม่มีของ" กับ "ด่านกรองตีตก" กับ "ตัดทิ้งก่อนถึงด่าน" เป็นคนละอาการ วิธีแก้คนละทาง
  5. ซ่อมเสร็จแล้วเติมด่านตรวจผลลัพธ์ไว้ท้ายสุดทุกครั้ง ให้รอบหน้ามันขึ้นแดงเอง ไม่ต้องรอคนมาทัก

สรุปสิ่งที่เปลี่ยน

เรื่องนี้ไม่มีบั๊กที่หน้าตาเหมือนบั๊กเลยสักตัว ทุกอันคือโค้ดที่ทำงานถูกตามที่เขียน แต่สมมติฐานที่อยู่ข้างใต้มันผิด ทั้ง "ฟีดเรียงตามวันที่ให้แล้ว" ทั้ง "สตริงวันที่เรียงได้" ทั้ง "ขอมาน้อยลงคือประหยัด" สามข้อนี้ฟังดูสมเหตุสมผลทั้งหมดตอนเขียน และไม่มีข้อไหนที่จะส่งเสียงตอนมันผิด

ส่วนตัวผมถ้าให้เลือกทำได้แค่อย่างเดียวจากทั้งหมดนี้ ผมเลือกด่านตรวจความสดในหัวข้อที่แล้ว เพราะอีกสามข้อคือการแก้ปัญหาที่เจอแล้ว แต่ข้อนั้นคือสิ่งที่จะบอกผมเองว่าเจอปัญหาใหม่ที่ยังไม่รู้จัก ที่เหลือใครจะจัดลำดับยังไงก็ตามสะดวกครับ

ปล. ถ้าต้นทางของใครออกของไม่สม่ำเสมอจริงๆ เช่นบางสัปดาห์เงียบไปทั้งอาทิตย์ อย่าเพิ่งตั้งด่านความสดให้ล้ม ให้เริ่มจากส่งเตือนอย่างเดียวก่อนสักสองสามสัปดาห์ จับจังหวะจริงของต้นทางให้ได้ก่อน แล้วค่อยตั้งเส้น ไม่งั้นจะได้แจ้งเตือนลวงจนคนเลิกอ่าน เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ

เคยเจองานแบบนี้ไหม? ติดตรงไหน?

มาแลกวิธีกันต่อในบอร์ด LinuxThai

→ คุยต่อในบอร์ด

ความคิดเห็น 0

ยังไม่มีความคิดเห็น — เป็นคนแรกเลย