Τεχνικό Άρθρο

HotPDF RenderCacheFolder: cache σελίδων σε δίσκο στο Delphi

Το HotPDF RenderCacheFolder γυρνά τη cache αποδιδομένων σελίδων στη μνήμη του HotPDF Delphi component σε μόνιμο disk page cache: οι αποδιδομένες σελίδες γράφονται ως PNG αρχεία κάτω από φάκελο που διαλέγετε, και την επόμενη φορά που ανοίγει η ίδια πηγή PDF, το RenderLoadedPageToBitmapCached τα διαβάζει πίσω αντί να ραστεροποιήσει ξανά. Η σειρά αναζήτησης είναι μνήμη, μετά δίσκος, μετά ο renderer

Το στρώμα δίσκου βρίσκεται στο API από το v2.416.0, αλλά μέχρι το v2.770.140 δεν σερβίρισε ποτέ στην πραγματικότητα μια σελίδα για κανονική κλήση LoadFromFile ή LoadFromStream. Η διόρθωση ανάγκασε μια ερώτηση που κάθε μόνιμη cache πρέπει να απαντήσει: πώς ξέρετε ότι το αρχείο που ανοίξατε σήμερα είναι το έγγραφο που αποδώσατε χθες, και τι πάθουν οι cached σελίδες όταν δεν είναι; Παρακάτω είναι οι απαντήσεις που έκρινε το HotPDF, συμπεριλαμβανομένου του πού αρνείται σκόπιμα να κρατήσει cache

Πώς δουλεύει το disk render cache του HotPDF;

Το disk render cache του HotPDF είναι ένα δεύτερο στρώμα πίσω από τη raster cache στη μνήμη, και συμμετέχει μόνο όταν το RenderCacheFolder είναι μη κενή διαδρομή. Μια κλήση RenderLoadedPageToBitmapCached(PageIndex, DPI) σαρώνει πρώτα τις εγγραφές στη μνήμη, κλειδωμένες ανά δείκτη σελίδας, DPI και παραλλαγή ρυθμίσεων απόδοσης. Σε αστοχία ρωτάει το στρώμα δίσκου· ένα χτύπημα δίσκου αποκωδικοποιεί το PNG, το προάγει πίσω στη μνήμη και επιστρέφει αντίγραφο ιδιοκτησίας του καλούντος. Μόνο όταν και τα δύο στρώματα χάσουν η σελίδα περνά από τον διερμηνέα content-stream που περιγράφεται στο rendering μιας φορτωμένης σελίδας PDF σε TBitmap, και το φρέσκο bitmap γράφεται τότε και στον δίσκο

Διάγραμμα HotPDF της αναζήτησης render cache για το RenderLoadedPageToBitmapCached: το στρώμα μνήμης κλειδωμένο ανά σελίδα, DPI και παραλλαγή απόδοσης ελέγχεται πρώτο, μετά το στρώμα δίσκου RenderCacheFolder από PNG αρχεία με ατομική αντικατάσταση, μετά ο διερμηνέας content-stream, και κάθε χτύπημα επιστρέφει αντίγραφο ιδιοκτησίας του καλούντος
Το HotPDF κοιτάει πρώτα στη μνήμη, μετά στον δίσκο, και μόνο τότε ραστεροποιεί· ένα χτύπημα δίσκου προάγεται πίσω στη μνήμη και κάθε διαδρομή σας παραδίδει αντίγραφο που ανήκετε εσείς και πρέπει να απελευθερώσετε

Στον δίσκο η διάταξη είναι σκόπιμα βαρετή. Κάθε έγγραφο παίρνει υποφάκελο με όνομα από ένα document key 16 δεκαεξαδικών χαρακτήρων συν μια παραλλαγή απόδοσης 16 δεκαεξαδικών χαρακτήρων, κάθε σελίδα αποθηκεύεται ως <page>@<dpi>.png, και ένα index.txt στη ρίζα κρατά τα έγγραφα σε σειρά πιο πρόσφατης χρήσης πίσω από tag schema. Ασυμφωνία schema καθαρίζει τον φάκελο στην πρώτη χρήση. Οι εγγραφές πάνε πρώτα σε προσωρινό αρχείο και ανταλλάσσονται στη θέση τους με ατομική αντικατάσταση, οπότε ένα crash στη μέση εγγραφής αφήνει είτε την παλιά σελίδα είτε τίποτα, ποτέ μισό PNG. Ένα PNG που αποτυγχάνει να αποκωδικοποιηθεί διαγράφεται και μετράει ως αστοχία

Τρία όρια οριοθετούν τον φάκελο:

  • Το RenderCacheMaxDocuments (προεπιλογή 20) περικόπτει το πλήθος των υποφακέλων εγγράφων· ο φάκελος λιγότερο πρόσφατα χρησιμοποιημένος εκδιώκεται πρώτος
  • Το RenderCacheMaxBytes (προεπιλογή 524288000, δηλαδή 500 MB) περικόπτει το συνολικό μέγεθος όλων των PNG αρχείων κάτω από τη ρίζα
  • Κάθε φάκελος εγγράφου κρατά το πολύ 200 εικόνες σελίδων· εκείνο το ανά έγγραφο όριο είναι σταθερό από το THotPDF και δεν είναι δημοσιευμένη ιδιότητα

Το RenderCacheCapacity (προεπιλογή 8) είναι ξεχωριστό knob: ορίζει πόσες αποδιδομένες σελίδες κρατά το στρώμα μνήμης, και δεν έχει καμία σχέση με το disk footprint

uses
  SysUtils, Graphics, HPDFDoc;

procedure WarmThumbnails(const FileName: string);
var
  Pdf: THotPDF;
  Bmp: TBitmap;
  I: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    // Ρυθμίστε το στρώμα δίσκου πριν το πρώτο cached render:
    // ο φάκελος και τα δύο όρια διαβάζονται όταν το στρώμα χρησιμοποιηθεί πρώτη φορά
    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
          // Παραδώστε το αντίγραφο στη λωρίδα thumbnails εδώ
        finally
          Bmp.Free; // η cached κλήση επιστρέφει πάντα αντίγραφο ιδιοκτησίας του καλούντος
        end;
      end;
  finally
    Pdf.Free; // από το v2.770.140 αυτό δεν διαγράφει πια τις εγγραφές δίσκου
  end;
end;

Τρέξτε την ίδια διαδικασία δύο φορές και το δεύτερο τρέξιμο δεν ραστεροποιεί ποτέ σελίδα που χωρούσε στην cache. Το αντικείμενο disk cache δημιουργείται τεμπέλικα στο πρώτο cached render και ζει μέχρι το instance THotPDF να απελευθερωθεί, οπότε αλλαγή του RenderCacheFolder, RenderCacheMaxDocuments ή RenderCacheMaxBytes μετά από εκείνο το σημείο δεν μετακινεί ούτε αναδιαστασιολογεί ήδη ανοιγμένη cache. Σελίδες πολύ μεγάλες για την πολιτική εισδοχής μνήμης (by default μία εγγραφή δεν υπερβαίνει τα 64 MiB pixels 32 bit) δεν αποθηκεύονται ούτε στον δίσκο, και το στρώμα δίσκου συμβουλεύεται μόνο όσο το RenderFallbackPolicy κρατά την προεπιλογή rfpIgnore, επειδή τα διαγνωστικά fallback δεν αποθηκεύονται δίπλα στο PNG

Γιατί το RenderCacheFolder δεν δούλεψε ποτέ πριν το v2.770.140;

Το RenderCacheFolder δεν είχε κανένα αποτέλεσμα πριν το v2.770.140 επειδή το στρώμα δίσκου κλειδωνε τα έγγραφα σε hash των source bytes που οι συνηθισμένες φορτώσεις δεν κρατούσαν ποτέ. Το document key ερχόταν από SHA-256 πάνω σε εσωτερικό αντίγραφο των ωμών bytes PDF, αλλά τα LoadFromFile και LoadFromStream κάνουν parse την πηγή στη θέση της και δεν διατηρούν τέτοιο αντίγραφο· το πεδίο γεμιζόταν μόνο προσωρινά σε μια διαδρομή κρυπτογραφημένης ανάκτησης και μηδενιζόταν ξανά αμέσως μετά. Χωρίς bytes, το κλειδί ήταν πάντα κενό, και κενό κλειδί σημαίνει ότι το στρώμα δίσκου παρακάμπτεται. Κανένα σφάλμα, καμία προειδοποίηση, απλώς ένας φάκελος που έμενε άδειος

Το να γίνει το κλειδί μη κενό αποκάλυψε ένα δεύτερο bug που κρυβόταν πίσω από το πρώτο. Το παλιό InvalidateRenderedPageCache διέγραφε τον φάκελο δίσκου του εγγράφου, και το InvalidateRenderedPageCache τρέχει στην αρχή κάθε φόρτωσης, σε κάθε αλλαγή και μέσα στο Free. Άρα τη στιγμή που το κλειδί δούλευε, κάθε συνεδρία viewer θα κατέστρεφε τη δική του cache στην έξοδο, και η επόμενη συνεδρία θα ξεκινούσε κρύα ούτως ή άλλως. Χειρότερα, το κλειδί ξαναϋπολογιζόταν από την ίδια πηγή μετά από αλλαγή, οπότε renders του αλλαγμένου εγγράφου θα αποθηκεύονταν κάτω από το κλειδί του πρωτότυπου αρχείου και σερβίρονταν στην επόμενη συνεδρία που άνοιγε το τροποποιημένο PDF. Το v2.770.140 διορθώνει την ταυτότητα και την ακύρωση μαζί· η διόρθωση μόνο ενός θα παραδίδετε είτε νεκρή cache είτε ψεύτικη

Πώς αναγνωρίζει το HotPDF ένα PDF χωρίς να διαβάσει όλο το αρχείο

Το HotPDF αναγνωρίζει ένα PDF φορτωμένο από τοπικό αρχείο με ένα fingerprint του μεγέθους του, του last-write time του και των πρώτων και τελευταίων 64 KiB του, και αναγνωρίζει stream ή random-access πηγή με SHA-256 όλου του περιεχομένου της. Και τα δύο συλλαμβάνονται μία φορά, όταν μια φόρτωση πετύχει, και οι πρώτοι 16 δεκαεξαδικοί χαρακτήρες του digest SHA-256 (64 bits) γίνονται το document key

ΠηγήΤαυτότηταΚόστοςΣύλληψη όταν
LoadFromFileΜέγεθος + LastWriteTime + πρώτα και τελευταία 64 KiB, με hash SHA-256Το πολύ ανάγνωση 128 KiB, ανεξάρτητο από μέγεθος αρχείουΚάθε επιτυχημένη φόρτωση, ακόμα κι αν το RenderCacheFolder οριστεί μετά
LoadFromStreamSHA-256 όλου του streamΈνα πλήρες πέρασμα πάνω στην πηγήΜόνο αν το RenderCacheFolder είχε οριστεί πριν τη φόρτωση
LoadFromRandomAccessSourceSHA-256 όλης της πηγήςΈνα πλήρες πέρασμα πάνω στην πηγήΜόνο αν ο φάκελος είχε οριστεί πρώτα και όλο το εύρος είναι διαθέσιμο
Οποιαδήποτε πηγή με εγγραφή /EncryptΚαμίαΚανέναΠοτέ· το στρώμα δίσκου παρακάμπτεται
Χάρτης ταυτότητας πηγής HotPDF για το disk render cache: το LoadFromFile κάνει hash σε μέγεθος, LastWriteTime και πρώτα και τελευταία 64 KiB, το LoadFromStream και το LoadFromRandomAccessSource κάνουν hash όλο το περιεχόμενο μόνο όταν το RenderCacheFolder είχε οριστεί πρώτα, και οποιοδήποτε trailer /Encrypt δεν συλλαμβάνει καθόλου ταυτότητα
τα αρχεία παίρνουν fingerprint από τα άκρα τους επειδή το header, το xref και το trailer ζουν εκεί, τα streams πληρώνουν πλήρες hash μόνο όταν ζητήσατε πρώτα την cache, και τα κρυπτογραφημένα έγγραφα δεν γράφονται ποτέ στον δίσκο

Το fingerprint του αρχείου είναι σκόπιμος συμβιβασμός. Το πλήρες hashing ενός σαρωμένου αρχείου 400 MB σε κάθε άνοιγμα μπορεί να κοστίζει περισσότερο από την απόδοση των δύο σελίδων που κοιτάζει πραγματικά ένας χρήστης. Τα δειγματοληπτικά τμήματα δεν είναι τυχαία: το header κάθεται στην αρχή του αρχείου, και το trailer και η τελευταία ενότητα cross-reference κάθονται στο τέλος (ISO 32000-1 §7.5). Ένα incremental update προσαρτά νέο body, ενότητα cross-reference και trailer (§7.5.6), οπότε αλλάζει το μέγεθος και την ουρά μονομιάς. Μια πλήρης επανεγγραφή από οποιοδήποτε κανονικό εργαλείο αλλάζει το last-write time. Για αρχεία έως 128 KiB τα δύο δείγματα καλύπτουν κάθε byte, οπότε τα μικρά έγγραφα ουσιαστικά γίνονται hash στο σύνολό τους

Ο υπολειπόμενος κίνδυνος είναι μια αλλαγή ίδιου μεγέθους στη θέση της, στη μέση ενός μεγάλου αρχείου, του οποίου ο writer έπειτα επαναφέρει το αρχικό timestamp. Αυτό θέλει εργαλείο που διατηρεί σκόπιμα τα modification times ενώ επεξεργάζεται περιεχόμενο, που είναι σπάνιο αλλά όχι αδύνατο, και σε εκείνη την περίπτωση η cache σερβίρει παρωχημένες σελίδες. Η ανάποδη πλευρά είναι καλοπροαίρετη: η αντιγραφή ενός αρχείου στα Windows κανονικά διατηρεί το last-write time του, οπότε ένα αντίγραφο εγγράφου που βρίσκεται ήδη στην cache χτυπά τις ίδιες εγγραφές, που είναι σωστό επειδή τα bytes είναι πανομοιότυπα

Τα streams δεν έχουν καθόλου modification time, οπότε η μοναδική τίμια ταυτότητα είναι το περιεχόμενο. Το HotPDF πληρώνει εκείνο το πλήρες πέρασμα SHA-256 μόνο όταν έχετε ζητήσει disk cache πριν τη φόρτωση· κάθε άλλος καλών του LoadFromStream δεν βλέπει extra κόστος. Αυτό κάνει τη σειρά ανάθεσης της ιδιότητας κρίσιμη:

procedure OpenDownloadedPdf(Pdf: THotPDF; Data: TStream;
  const CacheRoot: string);
begin
  // Λάθος σειρά για streams: το hash περιεχομένου υπολογίζεται μόνο όταν ο
  // φάκελος είναι ήδη ορισμένος, οπότε αυτό το έγγραφο θα παρακαμφθεί το στρώμα δίσκου
  //   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 πηγή που ακόμα κατεβαίνει (κάποια εύρη μή διαθέσιμα) δεν παίρνει ταυτότητα αντί για hash μερικού περιεχομένου, και αν ο υπολογισμός της ταυτότητας αποτύχει για οποιονδήποτε λόγο η φόρτωση εξακολουθεί να πετυχαίνει· το έγγραφο απλώς αποδίδει χωρίς το στρώμα δίσκου

Τι ακυρώνει μια εγγραφή disk cache του HotPDF;

Μια εγγραφή disk cache του HotPDF δεν ακυρώνεται ποτέ με διαγραφή της σε αλλαγή· αντίθετα, η επεξεργασία του φορτωμένου εγγράφου ρίχνει την ταυτότητα του εγγράφου, οπότε το στρώμα δίσκου παρακάμπτεται για την υπόλοιπη εκείνη φόρτωση και οι αποθηκευμένες σελίδες μένουν έγκυρες για την τροποποιημένη πηγή. Οι εγγραφές φεύγουν από τον δίσκο μόνο μέσω των ορίων LRU και bytes, ενός κατεστραμμένου PNG, ή μιας αλλαγής schema

Το κλειδί περιγράφει μια πηγή στον δίσκο, όχι το object graph στη μνήμη. Μόλις σφραγίσετε μια σελίδα ή αλλάξετε annotation, το έγγραφο δεν ταιριάζει πια με εκείνη την πηγή, οπότε ούτε η ανάγνωση ούτε η εγγραφή κάτω από το κλειδί της θα ήταν σωστές. Από το v2.770.140, τόσο η ακύρωση επιπέδου εγγράφου όσο και επιπέδου σελίδας καθαρίζουν την ταυτότητα αντί να αγγίζουν τον φάκελο, και υπάρχει δεύτερος φρουρός για αλλαγές που δεν κάλεσαν InvalidateRenderedPageCache: πριν χρησιμοποιήσει το στρώμα δίσκου, το THotPDF ελέγχει αν κάποιο φορτωμένο object είναι dirty και μεταχειρίζεται dirty έγγραφο ως χωρίς ταυτότητα

Οι ρυθμίσεις απόδοσης δουλεύουν ανάποδα. Η αλλαγή PageRenderBackend (ή η κλήση UseNativeGDIRenderBackend), και η κλήση ConfigureRenderICCWorkflow ή ClearRenderICCWorkflow, ξεπλένουν τις σελίδες μνήμης αλλά κρατούν την ταυτότητα, επειδή το έγγραφο εξακολουθεί να ταιριάζει με την πηγή του. Εκείνες οι ρυθμίσεις αλλάζουν τα pixels χωρίς να ανήκουν στην παραλλαγή μνήμης, οπότε το disk key διπλώνει μέσα το όνομα backend, τη σημαία black-point compensation και digests SHA-256 των profiles ICC proof και εξόδου. Η ίδια η παραλλαγή καλύπτει ήδη το color intent, το output dithering, την προεπισκόπηση overprint, τη λειτουργία luminosity mask, την πολιτική fallback και την ορατότητα κάθε ομάδας optional content, οπότε η εναλλαγή ενός layer αποδίδει σε διαφορετικό φάκελο αντί να ξαναγράψει την προεπιλεγμένη προβολή

Σημασιολογική ακύρωσης HotPDF για το disk cache RenderCacheFolder: η επεξεργασία του φορτωμένου εγγράφου ή οποιουδήποτε dirty object ρίχνει την ταυτότητα πηγής ώστε το στρώμα παρακάμπτεται, η αλλαγή render backend ή ICC workflow κρατά ταυτότητα κάτω από νέο κλειδί παραλλαγής, και αποθήκευση συν επαναφόρτωση ξανακλειδώνει το έγγραφο
μια αλλαγή δεν διαγράφει ποτέ τον αποθηκευμένο φάκελο, μια αλλαγή ρυθμίσεων αποδίδει κάτω από διαφορετικό κλειδί, και μόνο αποθήκευση συν επαναφόρτωση χαρίζει στο αλλαγμένο έγγραφο φρέσκια ταυτότητα

Για να ξαναπάει ένα αλλαγμένο έγγραφο στο στρώμα δίσκου, δώστε του νέα ταυτότητα πηγής αποθηκεύοντας το και φορτώνοντας το αποτέλεσμα:

procedure CommitEditsAndRekey(Pdf: THotPDF; const EditedFile: string);
begin
  // Μετά την επεξεργασία του φορτωμένου εγγράφου: ανανεώστε τις σελίδες μνήμης.
  // Η ταυτότητα πηγής έχει ήδη φύγει, οπότε τίποτα δεν διαβάζεται από ούτε
  // γράφεται στον φάκελο δίσκου του αρχικού εγγράφου
  Pdf.InvalidateRenderedPageCache;

  // Ένα αποθηκευμένο αρχείο έχει νέο μέγεθος και last-write time, άρα νέα
  // ταυτότητα· renders μετά από αυτή τη φόρτωση γίνονται cache κάτω από το νέο κλειδί
  Pdf.SaveLoadedDocument(EditedFile);
  if Pdf.LoadFromFile(EditedFile) <= 0 then
    raise Exception.Create('Could not reload the edited document');
end;

Ο φάκελος του αρχικού εγγράφου μένει ήσυχος και γερνά μέσα από τα RenderCacheMaxDocuments και RenderCacheMaxBytes σαν κάθε άλλη εγγραφή. Αν ο χρήστης ξανανοίξει την αμετάβλητη αρχική, οι σελίδες της είναι ακόμα εκεί

Όρια ασφαλείας: κρυπτογραφημένες πηγές και linked φάκελοι

Το disk render cache του HotPDF αρνείται δύο είδη εισόδου επίτηδες: δεν γράφει ποτέ σελίδες κρυπτογραφημένου PDF στον δίσκο, και δεν ακολουθεί ποτέ υποφάκελο εγγράφου που είναι junction ή άλλο reparse point. Και οι δύο κανόνες ανταλλάσσουν χτυπήματα cache με το να μην διαρρέουν δεδομένα ή να μην διαγράφουν λάθος αρχεία

Κρυπτογραφημένα PDF δεν γίνονται ποτέ cache στον δίσκο

Μια αποδιδομένη σελίδα είναι αποκρυπτογραφημένο περιεχόμενο. Η εγγραφή της ως σκέτο PNG σε φάκελο cache θα άφηνε αναγνώσιμο αντίγραφο εγγράφου προστατευμένου με κωδικό στον δίσκο, έξω από την προστασία που διάλεξε ο συγγραφέας (ISO 32000-1 §7.6). Το HotPDF συλλαμβάνει επομένως καμία ταυτότητα για κάθε πηγή του οποίου το trailer κουβαλά εγγραφή /Encrypt, συμπεριλαμβανομένων αρχείων ανοιγμένων με κωδικό ή με κενό κωδικό χρήστη. Εκείνα τα έγγραφα εξακολουθούν να χρησιμοποιούν το στρώμα μνήμης, που πεθαίνει με τη διεργασία

Υποφάκελοι junction απορρίπτονται από το v2.770.173

Η ρίζα cache είναι δική σας επιλογή, και το να τη δείξετε σε junction επιτρέπεται. Οι υποφάκελοι εγγράφων κάτω από αυτήν είναι άλλη ιστορία: η cache τους δημιουργεί, τους διαβάζει, τους αγγίζει και τους διαγράφει μόνη της, κατά την ανάκτηση εκκίνησης (που αφαιρεί υπολείμματα προσωρινών αρχείων), στην αναζήτηση (που ενημερώνει timestamps), στην αποθήκευση, στην ακύρωση και στα τρία όρια εκδίωξης. Αν κάποιος με δικαίωμα εγγραφής στη ρίζα cache αντικαταστήσει φάκελο εγγράφου με junction προς άλλον κατάλογο, κάθε μία από εκείνες τις διαδρομές θα τον ακολουθούσε, και η εκδίωξη θα διέγραφε αρχεία κάπου που η cache δεν κατείχε ποτέ. Από το v2.770.173 κάθε ένα από εκείνα τα σημεία εισόδου ελέγχει το attribute reparse-point και παραλείπει linked φάκελο εγγράφου: μια αναζήτηση μετράει αστοχία, μια αποθήκευση μετράει αποτυχημένη εγγραφή, και η εκδίωξη τον αφήνει ήσυχο

Unicode διαδρομές και κοινές ρίζες

Δύο συγγενικές διορθώσεις μετράνε αν αναπτύσσετε σε προφίλ χρηστών. Πριν το v2.770.135, το RenderCacheFolder ήταν AnsiString, οπότε φάκελος έξω από τη code page του συστήματος (ελληνικό όνομα χρήστη σε αγγλική εγκατάσταση Windows, για παράδειγμα) μετατρεπόταν με απώλειες πριν το δει η cache· η ιδιότητα είναι πλέον Unicode string, και η ατομική αντικατάσταση χρησιμοποιεί το wide Windows API. Από το v2.770.52, πολλά instances THotPDF σε μία διεργασία που δείχνουν στην ίδια ρίζα (μετά την επέκταση διαδρομής, συγκρινόμενα case-insensitive) μοιράζονται ένα μοναδικό index και lock με reference count. Πριν, κάθε instance ξανάγραφε το index.txt με το δικό του αντίγραφο και επέβαλλε τα όρια απέναντι στη μερική του θέα, οπότε ο φάκελος μπορούσε να μεγαλώσει αρκετές φορές πάνω από το budget του

Εκείνος ο μοιρασμός σταματά στο όριο της διεργασίας. Δύο ξεχωριστές διεργασίες στην ίδια ρίζα κρατούν ακόμα ξεχωριστά indexes στη μνήμη, οπότε δώστε σε κάθε ταυτόχρονα τρεχούμενη εφαρμογή τη δική της ρίζα cache. Viewers που αποδίδουν σε worker threads είναι εντάξει μέσα σε μία διεργασία: το PrefetchLoadedPages και η ουρά που καλύπτεται στο background rendering με ουρά αιτημάτων περνούν και τα δύο από την ίδια cached διαδρομή και το ίδιο lock

Σύντομη αναφορά: λίστα ελέγχου RenderCacheFolder

  • Ορίστε RenderCacheFolder, RenderCacheMaxDocuments και RenderCacheMaxBytes πριν την πρώτη κλήση RenderLoadedPageToBitmapCached· για φορτώσεις stream και random-access, ορίστε τον φάκελο πριν τη φόρτωση
  • Αναβαθμίστε σε v2.770.140 ή νεότερο αν βασίζεστε στο στρώμα δίσκου· παλαιότερες εκδόσεις δέχονται την ιδιότητα αλλά δεν σερβίρουν ποτέ σελίδα από δίσκο για κανονικές φορτώσεις
  • Μην περιμένετε disk caching για κρυπτογραφημένα PDF, για έγγραφα επεξεργασμένα μετά τη φόρτωση, ή όσο το RenderFallbackPolicy δεν είναι rfpIgnore
  • Απελευθερώστε το instance THotPDF κανονικά· από το v2.770.140 ούτε το Free ούτε το InvalidateRenderedPageCache διαγράφουν εγγραφές δίσκου
  • Η αλλαγή PageRenderBackend ή του ICC workflow κρατά το έγγραφο στο στρώμα δίσκου κάτω από διαφορετικό κλειδί
  • Χρησιμοποιήστε μία ρίζα cache ανά τρεχούμενη εφαρμογή· τα instances μέσα σε μία διεργασία μοιράζονται το index από το v2.770.52
  • Κρατήστε τη ρίζα cache σε τοποθεσία ανά χρήστη· υποφάκελοι εγγράφων που είναι junctions παραλείπονται από το v2.770.173

Μια μόνιμη cache σελίδων αποδίδει περισσότερο σε viewer που ξανανοίγει τα ίδια έγγραφα όλη μέρα, που είναι ακριβώς το σχήμα της αρχιτεκτονικής custom PDF viewer σε Delphi που περιγράφεται αλλού σε αυτό το blog. Το RenderCacheFolder, η raster cache μνήμης και ο renderer σελίδων έρχονται με το HotPDF Delphi PDF component για Delphi και C++Builder