內容

名稱

Memoize::Expire - 自動過期記憶值的外掛模組

語法

  use Memoize;
  use Memoize::Expire;
  tie my %cache => 'Memoize::Expire',
	  	     LIFETIME => $lifetime,    # In seconds
		     NUM_USES => $n_uses;

  memoize 'function', SCALAR_CACHE => [HASH => \%cache ];

說明

Memoize::Expire 是 Memoize 的外掛模組。它允許自動過期記憶化函式的快取值。本手冊假設您已熟悉 Memoize 模組。如果不是,您應該先仔細研讀該手冊,特別注意 HASH 功能。

Memoize::Expire 是您可以在 Memoize 本身和實作快取的任何基礎套件之間插入的一層軟體。該層會提供一個雜湊變數,其值會在過於老舊、使用次數過多,或兩者皆是時過期。您告訴 Memoize 使用這個會遺忘的雜湊作為其快取,而不是預設的普通雜湊。

若要指定即時逾時,請提供一個數字值給 LIFETIME 選項。快取資料將在這麼多秒後過期,並在過期時重新查詢。當重新查詢資料項目時,其生命週期會重設。

如果您指定 NUM_USES 並帶有參數 n,則每次快取資料項目在您存取它第 n 次後,它將會被捨棄並重新查詢。當資料項目被重新查詢時,它的使用次數會被重設。

如果您指定兩個參數,資料將會在任一過期條件成立時從快取中捨棄。

Memoize::Expire 在內部使用真正的雜湊來儲存快取資料。您可以使用 Memoize::Expire 的 HASH 選項來提供一個繫結雜湊,以取代 Memoize::Expire 通常會使用的普通雜湊。您可以使用此功能將 Memoize::Expire 加入持恆磁碟雜湊和 Memoize 之間的層級。如果您這樣做,您將會取得一個條目會自動過期的持恆磁碟快取。例如

  #   Memoize
  #      |
  #   Memoize::Expire  enforces data expiration policy
  #      |
  #   DB_File  implements persistence of data in a disk file
  #      |
  #   Disk file

  use Memoize;
  use Memoize::Expire;
  use DB_File;

  # Set up persistence
  tie my %disk_cache => 'DB_File', $filename, O_CREAT|O_RDWR, 0666];

  # Set up expiration policy, supplying persistent hash as a target
  tie my %cache => 'Memoize::Expire', 
	  	     LIFETIME => $lifetime,    # In seconds
		     NUM_USES => $n_uses,
                     HASH => \%disk_cache; 

  # Set up memoization, supplying expiring persistent hash for cache
  memoize 'function', SCALAR_CACHE => [ HASH => \%cache ];

介面

Memoize::Expire 沒有什麼特別之處。它只是一個範例。如果您不喜歡它實作的政策,您可以自由撰寫您自己的過期政策模組,以實作您想要的任何政策。以下是如何執行此操作。假設您的模組將命名為 MyExpirePolicy。

簡要摘要:您需要建立一個定義四個方法的套件

TIEHASH

建構並傳回快取物件。

EXISTS

給定函式參數,對應的函式值是否在快取中,如果是,它是否足夠新以供使用?

FETCH

給定函式參數,在快取中查詢對應的函式值並傳回它。

STORE

給定函式參數和對應的函式值,將它們儲存到快取中。

CLEAR

(選用)完全清除快取。

想要快取記憶體根據您的政策過期的使用者會透過撰寫以下內容來表示

tie my %cache => 'MyExpirePolicy', args...;
memoize 'function', SCALAR_CACHE => [HASH => \%cache];

這將呼叫 MyExpirePolicy->TIEHASH(args)。MyExpirePolicy::TIEHASH 應執行任何適當的動作來設定快取,並且它應將快取物件傳回給呼叫者。

例如,MyExpirePolicy::TIEHASH 可能會建立一個包含一般 Perl 雜湊(它將用來儲存快取值)和一些關於參數、資料有多舊等額外資訊的物件。我們稱呼此物件為 C

當 Memoize 需要檢查快取中是否已有某個項目時,它會呼叫 C->EXISTS(key)key 是正規化的函式引數。如果 key 不在快取中,或已過期,MyExpirePolicy::EXISTS 應傳回 0;如果快取中有一個未過期的值,則傳回 1。它不應傳回 undef,因為某些版本的 Perl 中有一個錯誤,如果 EXISTS 方法傳回 undef,會導致錯誤的 FETCH。

如果 EXISTS 函式傳回 true,Memoize 會呼叫 C->FETCH(key) 來取得快取值。MyExpirePolicy::FETCH 應傳回快取值。否則,Memoize 會呼叫已快取的函式來計算適當的值,並呼叫 C->STORE(key, value) 將其儲存在快取中。

以下是一個非常簡短的政策模組範例,它會在十秒後讓每個快取項目過期。

	package Memoize::TenSecondExpire;

	sub TIEHASH {
	  my ($package, %args) = @_;
          my $cache = $args{HASH} || {};
	  bless $cache => $package;
	}

	sub EXISTS {
	  my ($cache, $key) = @_;
	  if (exists $cache->{$key} && 
              $cache->{$key}{EXPIRE_TIME} > time) {
	    return 1
	  } else {
	    return 0;  # Do NOT return undef here
	  }
	}

	sub FETCH {
	  my ($cache, $key) = @_;
	  return $cache->{$key}{VALUE};
	}

	sub STORE {
	  my ($cache, $key, $newvalue) = @_;
	  $cache->{$key}{VALUE} = $newvalue;
	  $cache->{$key}{EXPIRE_TIME} = time + 10;
	}

若要使用此過期政策,使用者會這麼說

	use Memoize;
        tie my %cache10sec => 'Memoize::TenSecondExpire';
	memoize 'function', SCALAR_CACHE => [HASH => \%cache10sec];

當快取值完全不存在或超過十秒時,Memoize 會呼叫 function

你應始終支援 TIEHASHHASH 引數,它會繫結底層快取,以便使用者可以指定快取也具有持續性或具有其他有趣的語意。上述範例示範了如何執行此操作,而 Memoize::Expire 也是如此。

另一個範例模組 Memoize::Saves 可在 CPAN 上的獨立發行版中取得。它實作了一個政策,讓你能夠指定某些函式值將永遠重新查詢。請參閱文件以取得詳細資訊。

其他選項

Brent Powers 有個 Memoize::ExpireLRU 模組,設計用於與 Memoize 搭配使用,並提供最近最少使用資料的過期功能。快取會保留固定數量的項目,當有新資料進來時,會讓最近最少使用的資料過期。

Joshua Chamas 的 Tie::Cache 模組可用作過期管理員。(如果你嘗試這個,請告訴我結果如何。)

如果你開發出任何有用的過期管理員,你認為應該與 Memoize 一起發行,請告訴我。

注意事項

此模組為實驗性質,可能含有錯誤。請將錯誤報告至以下地址。

使用次數儲存為 16 位元無符號整數,因此不能超過 65535。

由於時脈顆粒度,過期時間可能會比你預期的早一秒發生。例如,假設你儲存一個生命週期為十秒的值,並在某一天的 12:00:00.998 儲存它。Memoize 會查看時脈並看到 12:00:00。然後在 9.01 秒後,也就是 12:00:10.008 時,你嘗試讀取它。Memoize 會查看時脈並看到 12:00:10,並推論該值已過期。如果你已安裝 Time::HiRes,這可能不會發生。

作者

Mark-Jason Dominus

Mike Cariaso 提供了有價值的見解,說明解決此問題的最佳方式。

另請參閱

perl(1)

Memoize 手冊頁。