บทความเทคนิค

RenderCacheFolder ของ HotPDF: cache หน้า render บนดิสก์

RenderCacheFolder ของ HotPDF เปลี่ยน rendered-page cache ในหน่วยความจำของ HotPDF Delphi component ให้กลายเป็น page cache ถาวรบนดิสก์: หน้าที่ render แล้วถูกเขียนเป็นไฟล์ PNG ใต้โฟลเดอร์ที่คุณเลือก และครั้งถัดไปที่เปิด source PDF ตัวเดิม RenderLoadedPageToBitmapCached จะอ่านพวกมันกลับมาแทนการ rasterize ซ้ำ ลำดับค้นหาคือหน่วยความจำก่อน แล้วดิสก์ แล้วจึง renderer

ชั้นดิสก์อยู่ใน API มาตั้งแต่ v2.416.0 แต่ก่อน v2.770.140 มันไม่เคยส่งหน้าไหนกลับมาให้ call LoadFromFile หรือ LoadFromStream ปกติเลย การแก้บังคับคำถามที่ cache ถาวรทุกตัวต้องตอบ: คุณรู้ได้อย่างไรว่าไฟล์ที่เปิดวันนี้คือเอกสารที่ render ไว้เมื่อวาน และหน้าที่ cache ไว้เป็นอย่างไรเมื่อมันไม่ใช่ ด้านล่างคือคำตอบที่ HotPDF เลือกใช้ รวมถึงจุดที่มันตั้งใจปฏิเสธไม่ cache

disk render cache ของ HotPDF ทำงานอย่างไร

disk render cache ของ HotPDF เป็นชั้นที่สองต่อจาก raster cache ในหน่วยความจำ และมันเข้าร่วมเมื่อ RenderCacheFolder เป็น path ที่ไม่ว่างเท่านั้น การเรียก RenderLoadedPageToBitmapCached(PageIndex, DPI) จะสแกน entry ในหน่วยความจำก่อน โดยใช้ page index, DPI และเวอร์ชันของ render settings เป็น key เมื่อไม่เจอจะถามชั้นดิสก์ ถ้าเจอบนดิสก์ก็ decode PNG, ยกกลับขึ้นหน่วยความจำ แล้วคืนสำเนาให้ caller เป็นเจ้าของ เมื่อทั้งสองชั้นไม่มีหน้าจึงไหลผ่าน content-stream interpreter ที่เล่าไว้ในการ render หน้า PDF ที่โหลดแล้วเป็น TBitmap และ bitmap ที่ได้ใหม่ก็ถูกเขียนลงดิสก์ด้วย

แผนภาพ lookup ของ render cache ใน HotPDF สำหรับ RenderLoadedPageToBitmapCached: ชั้นหน่วยความจำที่ใช้ page DPI และ render variant เป็น key ถูกเช็คก่อน ต่อด้วยชั้นดิสก์ RenderCacheFolder ของไฟล์ PNG ที่แทนที่แบบ atomic ต่อด้วย content-stream interpreter และทุกเส้นทางที่เจอคืนสำเนาให้ caller เป็นเจ้าของ
HotPDF มองหน่วยความจำก่อน แล้วดิสก์ แล้วจึง rasterize ของที่เจอบนดิสก์ถูกยกกลับขึ้นหน่วยความจำ และทุกเส้นทางส่งสำเนาที่คุณเป็นเจ้าของและต้อง free เอง

บนดิสก์ layout ถูกออกแบบให้น่าเบื่อตั้งใจ แต่ละเอกสารได้โฟลเดอร์ย่อยตั้งชื่อจาก document key 16 ตัวอักษรเลขฐานสิบหกบวก render variant อีก 16 ตัว แต่ละหน้าเก็บเป็น <page>@<dpi>.png และ index.txt ที่รากคงรายการเอกสารไว้ตามลำดับใช้ล่าสุดหลัง tag ของ schema schema ไม่ตรงจะล้างโฟลเดอร์ตอนใช้ครั้งแรก การเขียนลงไฟล์ชั่วคราวก่อน แล้วสลับเข้าที่ด้วย atomic replace การ crash กลางทางจึงเหลือไว้แค่หน้าเดิมหรือไม่มีอะไรเลย ไม่มี PNG ครึ่ง ๆ กลาง ๆ PNG ที่ decode ไม่ผ่านถูกลบและนับเป็น miss

สามขีดจำกัดผูกโฟลเดอร์ไว้:

  • RenderCacheMaxDocuments (ค่าเริ่มต้น 20) จำกัดจำนวนโฟลเดอร์ย่อยของเอกสาร โฟลเดอร์ที่ใช้ล่าสุดเก่าสุดถูกไล่ออกก่อน
  • RenderCacheMaxBytes (ค่าเริ่มต้น 524288000 หรือ 500 MB) จำกัดขนาดรวมของไฟล์ PNG ทั้งหมดใต้ราก
  • โฟลเดอร์ของแต่ละเอกสารเก็บภาพหน้าได้ไม่เกิน 200 ภาพ cap ต่อเอกสารนี้ fix ตายตัวไว้ใน THotPDF ไม่ใช่ property ที่ประกาศให้ใช้

RenderCacheCapacity (ค่าเริ่มต้น 8) เป็นปุ่มแยกต่างหาก: มันเซ็ตว่าชั้นหน่วยความจำเก็บหน้าที่ render แล้วได้กี่หน้า ไม่เกี่ยวอะไรกับเนื้อที่บนดิสก์เลย

uses
  SysUtils, Graphics, HPDFDoc;

procedure WarmThumbnails(const FileName: string);
var
  Pdf: THotPDF;
  Bmp: TBitmap;
  I: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    // ตั้งค่า disk tier ก่อน render แบบ cached ครั้งแรก:
    // โฟลเดอร์กับ limit ทั้งคู่ถูกอ่านตอน tier ถูกใช้ครั้งแรก
    Pdf.RenderCacheFolder := IncludeTrailingPathDelimiter(
      GetEnvironmentVariable('LOCALAPPDATA')) + 'MyViewer\PageCache';
    Pdf.RenderCacheMaxDocuments := 50;
    Pdf.RenderCacheMaxBytes := Int64(1024) * 1024 * 1024; // 1 GiB
    Pdf.RenderCacheCapacity := 16;                        // หน้าในหน่วยความจำ

    if Pdf.LoadFromFile(FileName) > 0 then
      for I := 0 to Pdf.LoadedPageCount - 1 do
      begin
        Bmp := Pdf.RenderLoadedPageToBitmapCached(I, 96);
        if Bmp <> nil then
        try
          // เอาสำเนาไปให้แถบ thumbnail ตรงนี้
        finally
          Bmp.Free; // call แบบ cached คืนสำเนาให้ caller เป็นเจ้าของเสมอ
        end;
      end;
  finally
    Pdf.Free; // ตั้งแต่ v2.770.140 ตรงนี้ไม่ลบ entry บนดิสก์อีกแล้ว
  end;
end;

รัน procedure เดิมสองรอบ รอบที่สองจะไม่ rasterize หน้าไหนที่พอใน cache object ของ disk cache ถูกสร้างแบบ lazy ตอน render แบบ cached ครั้งแรกและอยู่จนกว่า instance ของ THotPDF จะถูก free การเปลี่ยน RenderCacheFolder, RenderCacheMaxDocuments หรือ RenderCacheMaxBytes หลังจุดนั้นจึงไม่ย้ายหรือปรับขนาด cache ที่เปิดอยู่แล้ว หน้าที่ใหญ่เกินนโยบายรับเข้าของหน่วยความจำ (ค่าเริ่มต้น entry เดียวห้ามเกิน 64 MiB ของ pixel 32 บิต) ไม่ถูกเก็บถาวรด้วย และชั้นดิสก์ถูกปรึกษาเฉพาะขณะที่ RenderFallbackPolicy ยังคงค่าเริ่มต้น rfpIgnore เพราะ diagnostic ของ fallback ไม่ได้เก็บร่วมกับ PNG

ทำไม RenderCacheFolder ถึงไม่เคยทำงานก่อน v2.770.140

RenderCacheFolder ไร้ผลมาก่อน v2.770.140 เพราะชั้นดิสก์ใช้ hash ของไบต์ source มาทำ key ของเอกสาร ซึ่งการโหลดปกติไม่เคยเก็บไว้ document key มาจาก SHA-256 เหนือสำเนาภายในของไบต์ PDF ดิบ แต่ LoadFromFile กับ LoadFromStream parse source ในที่เดิมและไม่เก็บสำเนานั้นไว้ field ถูกเติมแค่ชั่วคราวบนเส้นทางกู้เอกสารเข้ารหัสแล้วล้างทิ้งทันที ไม่มีไบต์ key จึงว่างตลอด และ key ว่างแปลว่าชั้นดิสก์ถูก bypass ไป ไม่มี error ไม่มีคำเตือน มีแต่โฟลเดอร์ที่ว่างเปล่า

พอทำให้ key ไม่ว่าง บั๊กตัวที่สองที่ซุ่มอยู่หลังตัวแรกก็โผล่ InvalidateRenderedPageCache ตัวเก่าลบโฟลเดอร์บนดิสก์ของเอกสารทิ้ง และ InvalidateRenderedPageCache รันตอนต้นการโหลดทุกครั้ง ตอนแก้ไขทุกครั้ง และข้างใน Free ทันทีที่ key ทำงานได้ ทุก session ของ viewer จะทำลาย cache ของตัวเองตอนออก และ session ถัดไปก็เริ่มเย็นฉีดอยู่ดี แย่กว่านั้น key ถูกคำนวณใหม่จาก source เดิมหลังการแก้ไข render ของเอกสารที่แก้แล้วจึงจะถูกเก็บใต้ key ของไฟล์ต้นฉบับ แล้วส่งต่อให้ session ถัดไปที่เปิด PDF ที่ยังไม่ถูกแก้ v2.770.140 แก้ identity กับ invalidation พร้อมกัน แก้ทีละอย่างจะได้แค่ cache ตายหรือ cache ที่โกหก

HotPDF ระบุตัวตน PDF โดยไม่อ่านทั้งไฟล์อย่างไร

HotPDF ระบุตัวตน PDF ที่โหลดจากไฟล์ในเครื่องด้วย fingerprint จากขนาด เวลาเขียนล่าสุด และ 64 KiB แรกกับท้ายของไฟล์ ส่วน stream หรือ random-access source ถูกระบุด้วย SHA-256 ของเนื้อหาทั้งหมด ทั้งสองแบบถูกจับครั้งเดียวตอนโหลดสำเร็จ และ 16 ตัวอักษรแรกของ digest SHA-256 (64 บิต) กลายเป็น document key

Sourceตัวตนค่าใช้จ่ายจับตอนไหน
LoadFromFileขนาด + LastWriteTime + 64 KiB หัวกับท้าย แล้ว hash ด้วย SHA-256อ่านมากสุด 128 KiB ไม่ขึ้นกับขนาดไฟล์ทุกการโหลดที่สำเร็จ แม้ RenderCacheFolder จะถูกเซ็ตทีหลัง
LoadFromStreamSHA-256 ของ stream ทั้งเส้นวิ่งผ่าน source เต็มหนึ่งรอบเฉพาะเมื่อ RenderCacheFolder ถูกเซ็ตก่อนโหลด
LoadFromRandomAccessSourceSHA-256 ของ source ทั้งก้อนวิ่งผ่าน source เต็มหนึ่งรอบเฉพาะเมื่อเซ็ตโฟลเดอร์ก่อนและ range ทั้งหมดพร้อมใช้
source ใด ๆ ที่มี entry /Encryptไม่มีไม่มีไม่มีวันจับ ชั้นดิสก์ถูก bypass
แผนที่ source identity ของ HotPDF สำหรับ disk render cache: LoadFromFile hash ขนาด LastWriteTime กับ 64 KiB แรกและท้าย LoadFromStream กับ LoadFromRandomAccessSource hash เนื้อหาทั้งหมดเฉพาะเมื่อ RenderCacheFolder ถูกเซ็ตก่อน และ trailer ที่มี /Encrypt จะไม่จับ identity ใด ๆ เลย
ไฟล์ถูกพิมพ์นิ้วจากหัวท้าย เพราะ header, xref กับ trailer อาศัยอยู่ตรงนั้น stream ยอมจ่ายค่า hash เต็มก็ต่อเมื่อคุณขอ cache ไว้ก่อน และเอกสารเข้ารหัสไม่มีวันถูกเขียนลงดิสก์

fingerprint ของไฟล์เป็น trade-off ที่ตั้งใจ hashing ไฟล์สแกน 400 MB ทั้งไฟล์ทุกครั้งที่เปิด อาจแพงกว่าการ render สองหน้าที่ผู้ใช้กำลังมองอยู่จริง ๆ ช่วงที่สุ่มเก็บไม่ใช่ตัวเลขมั่ว: header นั่งอยู่หัวไฟล์ และ trailer กับส่วน cross-reference สุดท้ายนั่งอยู่ท้ายไฟล์ (ISO 32000-1 §7.5) incremental update จะต่อท้าย body ใหม่ ส่วน cross-reference ใหม่และ trailer ใหม่ (§7.5.6) ขนาดกับท้ายไฟล์จึงเปลี่ยนพร้อมกัน การเขียนใหม่ทั้งไฟล์ด้วยเครื่องมือปกติเปลี่ยนเวลาเขียนล่าสุด ไฟล์ที่สั้นกว่า 128 KiB สองช่วงครอบคลุมทุกไบต์ เอกสารเล็กจึงถูก hash เต็มไปในตัว

ความเสี่ยงที่เหลือคือการแก้กลางไฟล์ใหญ่ในที่เดิมด้วยขนาดเท่าเดิม แล้ว writer กู้ timestamp เดิมคืน เคสนี้ต้องใช้เครื่องมือที่ตั้งใจรักษา modification time ขณะแก้เนื้อหา หายากแต่ไม่ใช่เป็นไปไม่ได้ และถ้าเจอ cache ก็จะเสิร์ฟหน้าเก่า ด้านที่โอเคคือกรณีธรรมดา: การ copy ไฟล์บน Windows มักคงเวลาเขียนล่าสุดไว้ สำเนาของเอกสารที่อยู่ใน cache แล้วจึงชน entry เดิม ซึ่งถูกต้องเพราะไบต์เหมือนกันทุกตัว

stream ไม่มี modification time ให้พูดถึงเลย ตัวตนที่ซื่อสัตย์ที่สุดจึงเหลือเนื้อหา HotPDF จ่ายค่าวิ่ง SHA-256 เต็มรอบเมื่อคุณขอ disk cache ไว้ก่อนโหลดเท่านั้น caller อื่น ๆ ของ LoadFromStream ไม่เห็นค่าใช้จ่ายเพิ่ม ลำดับการ assign property จึงกลายเป็นเรื่องที่รับน้ำหนัก:

procedure OpenDownloadedPdf(Pdf: THotPDF; Data: TStream;
  const CacheRoot: string);
begin
  // ลำดับผิดสำหรับ stream: hash ของเนื้อหาถูกคำนวณเมื่อ
  // โฟลเดอร์ถูกเซ็ตไว้ก่อนแล้วเท่านั้น เอกสารนี้จึง bypass disk tier
  //   Pdf.LoadFromStream(Data);
  //   Pdf.RenderCacheFolder := CacheRoot;

  Pdf.RenderCacheFolder := CacheRoot; // เซ็ตก่อน
  Data.Position := 0;
  if Pdf.LoadFromStream(Data) <= 0 then
    raise Exception.Create('The stream is not a loadable PDF');
end;

random-access source ที่ยังกำลังดาวน์โหลด (บาง range ยังไม่พร้อม) จะไม่ได้ตัวตน แทนที่จะได้ hash ของเนื้อหาครึ่ง ๆ กลาง ๆ และถ้าการคำนวณตัวตนล้มเหตุใดก็ตาม การโหลดยังสำเร็จเหมือนเดิม เอกสารแค่ render โดยไม่มีชั้นดิสก์

อะไรทำให้ entry ของ disk cache ใน HotPDF เสียผล

entry ของ disk cache ใน HotPDF ไม่เคยถูกทำให้เสียผลด้วยการลบมันทิ้งตอนแก้ไข แต่การแก้เอกสารที่โหลดไว้จะทำ source identity หลุด ชั้นดิสก์จึงถูก bypass ตลอดช่วงโหลดนั้น และหน้าที่เก็บไว้ยังคงถูกต้องสำหรับ source ที่ไม่ถูกแก้ entry ออกจากดิสก์ด้วยเหตุเดียวคือขีดจำกัด LRU กับไบต์, PNG ที่เสีย หรือ schema เปลี่ยน

key บรรยาย source บนดิสก์ ไม่ใช่ object graph ในหน่วยความจำ พอคุณประทับตราหน้าหรือแก้ annotation เอกสารไม่ตรงกับ source นั้นอีก ทั้งการอ่านและเขียนใต้ key ของมันจึงไม่ถูกต้องอีก ตั้งแต่ v2.770.140 invalidation ทั้งระดับเอกสารและระดับหน้าล้างตัวตนแทนการแตะโฟลเดอร์ และยังมีกันสองชั้นสำหรับการแก้ที่ไม่ได้เรียก InvalidateRenderedPageCache: ก่อนใช้ชั้นดิสก์ THotPDF เช็คว่ามี object ที่โหลดไว้ตัวไหน dirty หรือเปล่า แล้วถือเอกสารที่ dirty เป็นเอกสารไร้ตัวตน

render settings ทำงานสวนทาง การสลับ PageRenderBackend (หรือเรียก UseNativeGDIRenderBackend) และการเรียก ConfigureRenderICCWorkflow หรือ ClearRenderICCWorkflow จะล้างหน้าในหน่วยความจำแต่คงตัวตนไว้ เพราะเอกสารยังตรงกับ source ของมัน การตั้งค่าพวกนี้เปลี่ยน pixel โดยไม่ได้เป็นส่วนของเวอร์ชันในหน่วยความจำ key บนดิสก์จึงห่อชื่อ backend, flag ของ black-point compensation และ digest SHA-256 ของ ICC proof กับ output profile เข้าไปด้วย เวอร์ชันเองครอบคลุม color intent, output dithering, overprint preview, luminosity mask mode, fallback policy และการมองเห็นของ optional content group ทุกตัวอยู่แล้ว การสลับ layer จึง render เข้าโฟลเดอร์คนละใบแทนการเขียนทับมุมมองค่าเริ่มต้น

semantics การทำให้เสียผลของ disk cache ใน RenderCacheFolder ของ HotPDF: การแก้เอกสารที่โหลดไว้หรือ object ที่ dirty ทำ source identity หลุดจนชั้นดิสก์ถูก bypass การเปลี่ยน render backend หรือ ICC workflow คงตัวตนไว้ใต้ key ของเวอร์ชันใหม่ และการ save แล้ว reload ให้ตัวตนใหม่กับเอกสารที่ถูกแก้
การแก้ไขไม่เคยลบโฟลเดอร์ที่เก็บไว้ การเปลี่ยนการตั้งค่า render ใต้ key ใหม่ และมีแต่ save แล้ว reload เท่านั้นที่หาตัวตนใหม่ให้เอกสารที่ถูกแก้

เอาเอกสารที่แก้แล้วกลับขึ้นชั้นดิสก์ ให้ตัวตนใหม่กับมันด้วยการ save แล้วโหลดผลลัพธ์:

procedure CommitEditsAndRekey(Pdf: THotPDF; const EditedFile: string);
begin
  // หลังแก้ไขเอกสารที่โหลดไว้: refresh หน้าในหน่วยความจำก่อน
  // source identity หายไปแล้ว จึงไม่มีการอ่านหรือเขียนอะไร
  // เข้าโฟลเดอร์บนดิสก์ของเอกสารต้นฉบับ
  Pdf.InvalidateRenderedPageCache;

  // ไฟล์ที่ save มีขนาดกับ last-write time ใหม่ จึงได้
  // identity ใหม่ render หลังโหลดครั้งนี้ถูก cache ใต้ key ใหม่
  Pdf.SaveLoadedDocument(EditedFile);
  if Pdf.LoadFromFile(EditedFile) <= 0 then
    raise Exception.Create('Could not reload the edited document');
end;

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

เขตความปลอดภัย: source เข้ารหัสกับโฟลเดอร์ที่ถูก link

disk render cache ของ HotPDF ปฏิเสธ input สองแบบตั้งใจ: มันไม่เคยเขียนหน้าของ PDF เข้ารหัสลงดิสก์ และไม่เคยไล่ตามโฟลเดอร์ย่อยของเอกสารที่เป็น junction หรือ reparse point อื่น ทั้งสองกฎแลก cache hit ไปแลกกับการไม่รั่วข้อมูลหรือลบไฟล์ผิดตัว

PDF เข้ารหัสไม่มีวันถูก cache บนดิสก์

หน้าที่ render แล้วคือเนื้อหาที่ถอดรหัสมาแล้ว การเขียนมันเป็น PNG เปล่า ๆ ลงโฟลเดอร์ cache คือการทิ้งสำเนาที่อ่านได้ของเอกสารที่ป้องกันด้วยรหัสผ่านค้างไว้บนดิสก์ นอกเหนือจากการป้องกันที่ผู้เขียนเลือกไว้ (ISO 32000-1 §7.6) HotPDF จึงไม่จับตัวตนให้ source ใดที่ trailer มี entry /Encrypt รวมถึงไฟล์ที่เปิดด้วยรหัสผ่านหรือรหัสผ่านผู้ใช้ว่าง เอกสารพวกนี้ยังใช้ชั้นหน่วยความจำได้ ซึ่งตายไปกับ process

โฟลเดอร์ย่อยแบบ junction ถูกปฏิเสธตั้งแต่ v2.770.173

รากของ cache เป็นดุลยพินิจของคุณ ชี้มันไปที่ junction ก็ทำได้ แต่โฟลเดอร์ย่อยของเอกสารใต้มันเป็นอีกเรื่อง cache เป็นคนสร้าง อ่าน แตะ และลบพวกมันเอง ทั้งตอน startup recovery (ซึ่งกวาดไฟล์ชั่วคราวที่ค้างทิ้ง) ตอน lookup (ซึ่งอัปเดต timestamp) ตอน store, invalidation และตอนไล่ออกตามขีดจำกัดทั้งสาม ถ้าใครสักคนที่มีสิทธิ์เขียนรากของ cache แทนโฟลเดอร์ของเอกสารด้วย junction ที่ชี้ไปไดเรกทอรีอื่น ทุกเส้นทางพวกนี้จะไล่ตามมันไป และ eviction จะไปลบไฟล์ในที่ที่ cache ไม่เคยเป็นเจ้าของ ตั้งแต่ v2.770.173 entry point เหล่านี้แต่ละจุดเช็ค attribute ของ reparse point แล้วข้ามโฟลเดอร์เอกสารที่ถูก link: lookup นับเป็น miss, store นับเป็นความล้มเหลวของการเขียน และ eviction ปล่อยมันไว้เฉย ๆ

path Unicode กับรากที่ใช้ร่วมกัน

การแก้ที่เกี่ยวเนื่องสองชิ้นสำคัญเมื่อคุณ deploy ลงโปรไฟล์ผู้ใช้ ก่อน v2.770.135 RenderCacheFolder เป็น AnsiString โฟลเดอร์ที่อยู่นอก system code page (ชื่อผู้ใช้จีนบน Windows ภาษาอังกฤษ เช่น) ถูกแปลงแบบเสียข้อมูลก่อน cache จะเห็นมัน property เป็น Unicode string แล้ว และ atomic replace ใช้ Windows API แบบ wide ตั้งแต่ v2.770.52 THotPDF หลาย instance ใน process เดียวที่ชี้รากเดียวกัน (หลังขยาย path เทียบแบบไม่สนตัวพิมพ์) แชร์ index และ lock แบบนับ reference ร่วมกันตัวเดียว ก่อนหน้านั้นแต่ละ instance เขียนทับ index.txt ด้วยสำเนาของตัวเองและบังคับขีดจำกัดกับมุมมองบางส่วนของมัน โฟลเดอร์จึงโตเกินงบประมาณได้หลายเท่า

การแชร์นี้หยุดที่ขอบ process สอง process แยกกันบนรากเดิมยังถือ index ในหน่วยความจำคนละชุด ให้แอปพลิเคชันที่รันพร้อมกันแต่ละตัวมีราก cache ของตัวเอง viewer ที่ render บน worker thread ก็ไม่มีปัญหาใน process เดียว PrefetchLoadedPages กับคิวงานที่เล่าไว้ในการ render เบื้องหลังด้วย request queueต่างเดินผ่านเส้นทาง cached เดียวกันและ lock เดียวกัน

สรุปด่วน: เช็คลิสต์ RenderCacheFolder

  • เซ็ต RenderCacheFolder, RenderCacheMaxDocuments กับ RenderCacheMaxBytes ก่อน call RenderLoadedPageToBitmapCached ครั้งแรก สำหรับการโหลดแบบ stream กับ random-access ให้เซ็ตโฟลเดอร์ก่อนโหลด
  • อัปเกรดเป็น v2.770.140 ขึ้นไปถ้าคุณพึ่งพาชั้นดิสก์ รุ่นก่อนหน้ารับ property ไปแต่ไม่เคยส่งหน้าจากดิสก์ให้การโหลดปกติเลย
  • อย่าคาดหวัง cache บนดิสก์สำหรับ PDF เข้ารหัส, เอกสารที่ถูกแก้หลังโหลด หรือขณะที่ RenderFallbackPolicy ไม่ใช่ rfpIgnore
  • free instance ของ THotPDF ตามปกติ ตั้งแต่ v2.770.140 ทั้ง Free และ InvalidateRenderedPageCache ไม่ลบ entry บนดิสก์แล้ว
  • การเปลี่ยน PageRenderBackend หรือ ICC workflow คงเอกสารไว้บนชั้นดิสก์ใต้ key ใหม่
  • ใช้ราก cache หนึ่งอันต่อแอปพลิเคชันที่รันอยู่ instance ข้างใน process เดียวแชร์ index กันตั้งแต่ v2.770.52
  • วางราก cache ไว้ในตำแหน่งต่อผู้ใช้ โฟลเดอร์ย่อยของเอกสารที่เป็น junction ถูกข้ามตั้งแต่ v2.770.173

page cache ถาวรคุ้มที่สุดกับ viewer ที่เปิดเอกสารชุดเดิมซ้ำทั้งวัน ซึ่งเป๊ะกับรูปทรงของสถาปัตยกรรม custom PDF viewer ใน Delphiที่เล่าไว้ที่อื่นในบล็อกนี้ RenderCacheFolder, raster cache ในหน่วยความจำและ page renderer มาพร้อมHotPDF Delphi PDF component สำหรับ Delphi และ C++Builder