🔗 存储管理器
🔗 简介
存储管理器是客户端和服务器端之间的粘合剂。缓存中保存的每个对象都被分配一个StoreEntry结构。在访问对象时,它还有一个MemObject结构。
Squid可以快速定位缓存的对象,因为它(在内存中)维护着所有StoreEntry的哈希表。哈希表的键是对象URI的MD5校验和。此外,还有一个存储策略,如LRU,用于跟踪对象并确定在需要回收空间时删除的顺序。对于LRU策略,这是作为一个双向链表实现的。
对于每个对象,StoreEntry通过sdirn和sfilen映射到一个cache_dir和位置。对于“ufs”存储,该文件号(sfilen)通过对L2和L1进行简单的模运算转换为磁盘路径名,但其他存储驱动程序可能会以其他方式映射sfilen。缓存交换文件由两部分组成:缓存元数据和对象数据。请注意,对象数据包括完整的HTTP回复——头部和主体。HTTP回复头部与缓存元数据不同。
客户端请求会向StoreEntry注册,以便在新数据到达时收到通知。多个客户端可以通过一个StoreEntry接收数据。对于POST和PUT请求,此过程反向工作。当从客户端读取更多数据时,会通知服务器端函数。
🔗 对象存储
待续……
🔗 对象检索
待续……
🔗 存储接口
🔗 简介
传统上,Squid一直使用Unix文件系统(UFS)在磁盘上存储缓存对象。多年来,UFS性能低下变得非常明显。在大多数情况下,UFS将Squid的性能限制在每秒大约30-50个请求。我们的工作表明,性能低下主要归因于open()和unlink()系统调用的同步性质,以及可能发生的inode/缓冲区缓存的颠簸。
我们想尝试使用我们自己的定制文件系统来处理Squid。为了做到这一点,我们需要一个定义明确的接口,用于Squid访问永久存储设备的各个部分。我们还需要每个存储模块对替换策略有更严格的控制,而不是单一的全局替换策略。
🔗 构建结构
存储类型位于squid/src/fs/。每个子目录对应存储类型的名称。当实现新的存储类型时,必须更新configure.in以从Makefile.in文件自动生成squid/src/fs/$type/目录中的Makefile。
configure将通过–enable-store-io参数接收存储类型的列表。此参数接受空格分隔的存储类型列表。例如,–enable-store-io=”ufs coss”。
每种存储类型都必须创建一个归档文件in squid/src/fs/$type.a。该文件在编译时自动链接到squid。
每个storefs必须导出一个名为storeFsSetup_$type()的函数。此函数在运行时调用,用于初始化每种存储类型。存储类型的列表通过store_modules.sh传递,以生成初始化函数storeFsSetup()。此函数位于store_modules.c中。
自动生成文件的示例
/* automatically generated by ./store_modules.sh ufs coss
* do not edit
*/
#include "squid.h"
extern STSETUP storeFsSetup_ufs;
extern STSETUP storeFsSetup_coss;
void storeFsSetup(void)
{
storeFsAdd("ufs", storeFsSetup_ufs);
storeFsAdd("coss", storeFsSetup_coss);
}
🔗 存储类型的初始化
每种存储类型通过storeFsSetup_$type()函数进行初始化。storeFsSetup_$type()函数接受一个参数——一个storefs_entry_t指针。此指针引用要初始化的storefs_entry。典型的设置函数如下所示:
void
storeFsSetup_ufs(storefs_entry_t *storefs)
{
assert(!ufs_initialised);
storefs->parsefunc = storeUfsDirParse;
storefs->reconfigurefunc = storeUfsDirReconfigure;
storefs->donefunc = storeUfsDirDone;
ufs_state_pool = memPoolCreate("UFS IO State data", sizeof(ufsstate_t));
ufs_initialised = 1;
}
storefs_entry中有五个函数指针需要初始化。在本例中,对设置函数被调用两次的情况进行了一些保护,并初始化了一个内存池供存储模块内部使用。
每个函数将在下面介绍。
🔗 完成
typedef void
STFSSHUTDOWN(void);
每当存储系统需要关闭时,就会调用此函数。它应该负责释放当前分配的所有资源。
typedef void
STFSPARSE(SwapDir *SD, int index, char *path);
typedef void
STFSRECONFIGURE(SwapDir *SD, int index, char *path);
这些函数负责配置和重新配置存储目录。可以通过调用strtok()和GetInteger()来检索cache_dir配置行中的其他参数。
STFSPARSE的任务是初始化一个新的swapdir。它应该解析cache_dir行中剩余的参数,初始化相关的函数指针和数据结构,并选择替换策略。STFSRECONFIGURE负责重新配置活动的swapdir。它应该解析cache_dir行中剩余的参数并更改任何活动的配置参数。实际的存储初始化通过SwapDir中的STINIT函数指针完成。
struct _SwapDir {
char *type; /* Pointer to the store dir type string */
int cur_size; /* Current swapsize in kb */
int low_size; /* ?? */
int max_size; /* Maximum swapsize in kb */
char *path; /* Path to store */
int index; /* This entry's index into the swapDir array */
int suggest; /* Suggestion for UFS style stores (??) */
size_t max_objsize; /* Maximum object size for this store */
union { /* Replacement policy-specific fields */
#ifdef HEAP_REPLACEMENT
struct {
heap *heap;
} heap;
#endif
struct {
dlink_list list;
dlink_node *walker;
} lru;
} repl;
int removals;
int scanned;
struct {
unsigned int selected:1; /* Currently selected for write */
unsigned int read_only:1; /* This store is read only */
} flags;
STINIT *init; /* Initialise the fs */
STNEWFS *newfs; /* Create a new fs */
STDUMP *dump; /* Dump fs config snippet */
STFREE *freefs; /* Free the fs data */
STDBLCHECK *dblcheck; /* Double check the obj integrity */
STSTATFS *statfs; /* Dump fs statistics */
STMAINTAINFS *maintainfs; /* Replacement maintainence */
STCHECKOBJ *checkob; /* Check if the fs will store an object, and get the FS load */
/* These two are notifications */
STREFOBJ *refobj; /* Reference this object */
STUNREFOBJ *unrefobj; /* Unreference this object */
STCALLBACK *callback; /* Handle pending callbacks */
STSYNC *sync; /* Sync the directory */
struct {
STOBJCREATE *create; /* Create a new object */
STOBJOPEN *open; /* Open an existing object */
STOBJCLOSE *close; /* Close an open object */
STOBJREAD *read; /* Read from an open object */
STOBJWRITE *write; /* Write to a created object */
STOBJUNLINK *unlink; /* Remove the given object */
} obj;
struct {
STLOGOPEN *open; /* Open the log */
STLOGCLOSE *close; /* Close the log */
STLOGWRITE *write; /* Write to the log */
struct {
STLOGCLEANOPEN *open; /* Open a clean log */
STLOGCLEANWRITE *write; /* Write to the log */
void *state; /* Current state */
} clean;
} log;
void *fsdata; /* FS-specific data */
};
🔗 存储模块的操作
Squid理解多个不同存储目录的概念。每个存储目录提供一个缓存对象存储,包含对象存储、检索、索引和替换。
每个打开的对象都有一个与之关联的storeIOState对象。storeIOState对象用于记录当前对象的状态。每个storeIOState都可以有一个特定于存储模块的数据结构,包含存储模块私有的信息。
struct _storeIOState {
sdirno swap_dirn; /* SwapDir index */
sfileno swap_filen; /* Unique file index number */
StoreEntry *e; /* Pointer to parent StoreEntry */
mode_t mode; /* Mode - O_RDONLY or O_WRONLY */
size_t st_size; /* Size of the object if known */
off_t offset; /* current _on-disk_ offset pointer */
STFNCB *file_callback; /* called on delayed sfileno assignments */
STIOCB *callback; /* IO Error handler callback */
void *callback_data; /* IO Error handler callback data */
struct {
STRCB *callback; /* Read completion callback */
void *callback_data; /* Read complation callback data */
} read;
struct {
unsigned int closing:1; /* debugging aid */
} flags;
void *fsstate; /* pointer to private fs state */
};
每个SwapDir都有一个最大对象大小的概念。这被用作存储层在首次选择合适的SwapDir时的基本提示。然后,对于合适的候选SwapDirs会调用checkobj函数,以确定它是否要存储给定的StoreEntry。maxobjsize为-1表示‘任意大小’。
SwapDir对象中列出的特定文件系统操作将在下面介绍。
🔗 initfs
typedef void STINIT(SwapDir *SD);
初始化给定的SwapDir。在此处执行验证和重建存储、创建任何所需位图等操作。
🔗 newfs
typedef void
STNEWFS(SwapDir *SD);
为每个配置的SwapDir调用,以执行文件系统初始化。当命令行中给出‘-z’给squid时,会发生这种情况。
🔗 dumpfs
typedef void
STDUMP(StoreEntry *e, SwapDir *SD);
将当前SwapDir的FS特定配置数据转储到给定的StoreEntry。用于从cachemgr接口获取配置文件转储。
注意:打印的选项应以空格字符开头,以将其与cache_dir路径分隔。
🔗 freefs
typedef void
STFREE(SwapDir *SD);
释放SwapDir文件系统信息。此例程应释放SD->fsdata。
🔗 doublecheckfs
typedef int
STDBLCHECK(SwapDir *SD, StoreEntry *e);
双重检查给定对象的有效性。在重建期间,如果命令行中给出‘-S’标志给squid,则调用。如果对象确实有效,则返回1,如果对象无效,则返回0。
🔗 statfs
typedef void
STSTATFS(SwapDir *SD, StoreEntry *e);
调用以检索文件系统统计信息,例如使用情况、负载和错误。信息应附加到传递的StoreEntry e。
🔗 maintainfs
typedef void
STMAINTAINFS(SwapDir *SD);
定期调用以替换对象。应使用活动的替换策略来超时未使用的对象,以便为新对象腾出空间。
🔗 回调
typedef void
STCALLBACK(SwapDir *SD);
此函数在comm_select/comm_poll循环中调用,以处理任何待定的回调。
🔗 同步
typedef void
STSYNC(SwapDir *SD);
每当需要同步到磁盘时,都会调用此函数。此函数在所有待处理数据已刷新到磁盘之前不应返回。
🔗 解析/重新配置
🔗 checkobj
typedef int
STCHECKOBJ(SwapDir *SD, const StoreEntry *e);
由storeDirSelectSwapDir()调用,以确定SwapDir是否要存储给定的StoreEntry对象。如果SwapDir不愿意存储给定的StoreEntry,则应返回-1。否则,应返回0到1000之间的值,表示当前的IO负载。值为1000表示SwapDir的IO负载为100%。storeDirSelectSwapDir()使用此值来选择IO负载最低的SwapDir。
🔗 referenceobj
typedef void
STREFOBJ(SwapDir *SD, StoreEntry *e);
每当对象被storeLockObject()锁定后调用。它通常用于更新对象在替换策略中的位置。
🔗 unreferenceobj
typedef void
STUNREFOBJ(SwapDir *SD, StoreEntry *e);
每当对象被storeUnlockObject()解锁并且锁定计数达到0时调用。它也通常用于更新对象在替换策略中的位置。
🔗 createobj
typedef storeIOState *
STOBJCREATE(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, STIOCB *io_callback, void *io_callback_data);
在SwapDir *SD中创建对象。每当文件系统分配或重新分配swap_filen时,就会调用file_callback。注意
- STFNCB使用通用的cbdata指针调用,该指针指向StoreEntry e。StoreEntry不应被修改,除了替换策略字段。
当发生错误并且对象关闭时,应调用IO回调。一旦调用了IO回调,storeIOState就变得无效。
STOBJCREATE成功时返回一个适合写入的storeIOState,如果发生错误则返回NULL。
🔗 openobj
typedef storeIOState *
STOBJOPEN(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, STIOCB *io_callback, void *io_callback_data);
在SwapDir *SD中打开StoreEntry以供读取。与STOBJCREATE类似,主要区别在于传递给file_callback的数据是相关的store_client。
🔗 closeobj
typedef void
STOBJCLOSE(SwapDir *SD, storeIOState *sio);
关闭已打开的对象。应在此例程的末尾调用STIOCB回调。
🔗 readobj
typedef void
STOBJREAD(SwapDir *SD, storeIOState *sio, char *buf, size_t size, off_t offset, STRCB *read_callback, void *read_callback_data);
将对象的_部分读取到buf中。当存在其他挂起的读写操作时,请求读取是安全的。STRCB在完成后被调用。
如果读取操作失败,文件系统层将通过调用具有错误状态代码的STIOCB回调来通知调用模块。
🔗 writeobj
typedef void
STOBJWRITE(SwapDir *SD, storeIOState *sio, char *buf, size_t size, off_t offset, FREE *freefunc);
将给定的数据块写入给定的存储对象。buf由调用者分配。写完成后,数据通过free_func释放。
如果写入操作失败,文件系统层将通过调用具有错误状态代码的STIOCB回调来通知调用模块。
🔗 unlinkobj
typedef void
STOBJUNLINK(SwapDir *, StoreEntry *);
从SwapDir SD和替换策略中删除StoreEntry e。
🔗 Store IO调用
这些例程用于存储管理器内部,从存储目录创建和检索对象。
🔗 storeCreate()
storeIOState *
storeCreate(StoreEntry *e, STIOCB *file_callback, STIOCB *close_callback, void * callback_data)
storeCreate被调用以将给定的StoreEntry存储在存储目录中。
callback是一个函数,当遇到错误或对象被关闭(通过调用storeClose())时会被调用。如果打开请求成功,则没有回调。调用模块必须假定打开请求会成功,并可以立即开始读写。
storeCreate()如果无法创建请求的对象,则可能返回NULL。在这种情况下,不会调用callback函数。
🔗 storeOpen()
storeIOState *
storeOpen(StoreEntry *e, STFNCB * file_callback, STIOCB * callback, void *callback_data)
storeOpen被调用以从其所在的存储目录中打开给定的StoreEntry。
callback是一个函数,当遇到错误或对象被关闭(通过调用storeClose())时会被调用。如果打开请求成功,则没有回调。调用模块必须假定打开请求会成功,并可以立即开始读写。
storeOpen()如果无法打开请求的对象,则可能返回NULL。在这种情况下,不会调用callback函数。
🔗 storeRead()
void
storeRead(storeIOState *sio, char *buf, size_t size, off_t offset, STRCB *callback, void *callback_data)
storeRead()比其他函数更复杂,因为它需要自己的回调函数来在请求的数据实际读取后通知调用者。buf必须是一个至少size字节的有效内存缓冲区。offset指定读取应开始的字节偏移量。请注意,由于缓存对象前面有Swap Meta Headers,因此此偏移量不等于实际对象数据的偏移量。
调用者负责分配和释放buf。
🔗 storeWrite()
void
storeWrite(storeIOState *sio, char *buf, size_t size, off_t offset, FREE *free_func)
storeWrite()提交一个请求,将数据块写入磁盘存储。调用者负责分配buf,但由于没有每个写入的回调,此内存必须由下层文件系统实现释放。因此,调用者必须指定用于取消分配内存的free_func。
如果写入操作失败,文件系统层将通过调用具有错误状态代码的STIOCB回调来通知调用模块。
🔗 storeUnlink()
void
storeUnlink(StoreEntry *e)
storeUnlink()从磁盘存储中删除缓存对象。没有回调函数,并且不需要先打开对象。文件系统层将在磁盘上删除该对象(如果存在)。
🔗 storeOffset()
off_t
storeOffset(storeIOState *sio)
storeOffset()返回当前的ondisk偏移量。这用于确定可以释放多少对象的内存以腾出空间给其他正在传输和缓存的对象。您必须确保storeIOState->offset指向磁盘偏移量,否则结果将是未定义的。对于读取,这返回成功读取数据的当前偏移量,不包括排队的读取。
🔗 回调
🔗 STIOCB回调
void
stiocb(void *data, int errorflag, storeIOState *sio)
stiocb函数作为参数传递给storeOpen()。文件系统层在发生I/O错误时,或在磁盘对象关闭时调用stiocb。
errorflag是以下之一:
#define DISK_OK (0)
#define DISK_ERROR (-1)
#define DISK_EOF (-2)
#define DISK_NO_SPACE_LEFT (-6)
一旦调用了stiocb函数,就不应再访问sio结构。
🔗 STRCB回调
void
strcb(void *data, const char *buf, size_t len)
strcb函数作为参数传递给storeRead()。在数据块从磁盘读取并放入buf后,文件系统层调用strcb。len指示了放入buf的字节数。仅当读取操作成功时,才会调用strcb函数。如果失败,则会调用STIOCB回调。
🔗 状态日志记录
这些函数处理Squid存储系统的状态日志记录及相关任务。这些函数在store_dir.c中使用(调用)。
每个存储系统都必须提供本节中描述的函数,尽管它可能是一个no-op(空操作)函数,什么也不做。每个函数都通过存储在SwapDir结构中的函数指针访问。
struct _SwapDir {
...
STINIT *init;
STNEWFS *newfs;
struct {
STLOGOPEN *open;
STLOGCLOSE *close;
STLOGWRITE *write;
struct {
STLOGCLEANOPEN *open;
STLOGCLEANWRITE *write;
void *state;
} clean;
} log;
....
};
🔗 log.open()
void
STLOGOPEN(SwapDir *);
类型为STLOGOPEN的log.open函数用于打开或初始化存储系统(如果有)的状态保持日志文件。对于UFS,它打开swap.state文件。
log.open函数可以在Squid执行期间被调用任意次数。例如,旋转或写入干净日志文件的过程会关闭状态日志然后重新打开它们。squid -k reconfigure也执行相同操作。
🔗 log.close()
void
STLOGCLOSE(SwapDir *);
类型为STLOGCLOSE的log.close函数显然是log.open的对应函数。它必须关闭存储系统(如果有)的已打开状态保持日志文件。
🔗 log.write()
void
STLOGWRITE(const SwapDir *, const StoreEntry *, int op);
类型为STLOGWRITE的log.write函数用于向状态保持日志文件写入一个条目。op参数是SWAP_LOG_ADD或SWAP_LOG_DEL。此功能可能不是某些存储系统必需的,可以实现为null函数(no-op)。
🔗 log.clean.start()
int
STLOGCLEANSTART(SwapDir *);
类型为STLOGCLEANSTART的log.clean.start函数用于写入“干净”状态保持日志文件的过程。干净写入过程由squid -k rotate命令启动。这是一个特殊情况,因为我们希望尽可能优化此过程。对于某些与UFS有相同日志记录问题的存储系统,这可能是一个no-op。
log.clean.state指针可能用于保留干净写入过程的状态信息,但不应被上层访问。
🔗 log.clean.nextentry()
StoreEntry *
STLOGCLEANNEXTENTRY(SwapDir *);
获取下一个可作为干净日志候选项的条目。
当没有更多对象可记录时返回NULL。
🔗 log.clean.write()
void
STLOGCLEANWRITE(SwapDir *, const StoreEntry *);
类型为STLOGCLEANWRITE的log.clean.write函数将一个条目写入干净日志文件(如果存在)。
🔗 log.clean.done()
void
STLOGCLEANDONE(SwapDir *);
表示干净写入过程的结束,并通知存储系统关闭干净日志,并重命名或移动它们,使其成为准备打开的官方状态保持日志。
🔗 替换策略实现
替换策略可以在STOBJREAD/STOBJWRITE/STOBJOPEN/STOBJCLOSE以及STREFOBJ和STUNREFOBJ期间更新。应注意仅修改StoreEntry中相关的替换策略条目。替换策略维护的责任已移交给每个SwapDir,以便存储代码能够严格控制替换策略。循环文件系统(如COSS)需要存储层和替换策略之间的紧密耦合。
🔗 删除策略API
删除策略负责确定当Squid需要为新对象回收空间时,按什么顺序删除对象。对象存储使用这样的策略来维护存储的对象并确定要删除什么来为新对象回收空间。(它们共同实现了替换策略)
🔗 API
它实现为一个模块化API,其中存储目录或内存创建一个选择的策略来维护其对象,并且模块注册以供此API使用。
🔗 createRemovalPolicy()
RemovalPolicy policy = createRemovalPolicy(cons char *type, cons char *args)
创建一个移除策略实例,其中可以维护对象优先级。
返回的RemovalPolicy实例是cbdata注册的。
🔗 policy.Free()
policy->Free(RemovalPolicy *policy)
销毁策略实例并释放所有相关内存。
🔗 policy.Add()
policy->Add(RemovalPolicy *policy, StoreEntry *, RemovalPolicyNode *node)
将StoreEntry添加到策略实例。
datap是指向存储条目的策略特定数据存储位置的指针,目前大小为一个(void *)指针。
🔗 policy.Remove()
policy->Remove(RemovalPolicy *policy, StoreEntry *, RemovalPolicyNode *node)
将StoreEntry从策略实例中按策略顺序移除。例如,当一个对象被更新的对象替换或手动从存储中清除时。
datap是指向存储条目的策略特定数据存储位置的指针,目前大小为一个(void *)指针。
🔗 policy.Referenced()
policy->Referenced(RemovalPolicy *policy, const StoreEntry *, RemovalPolicyNode *node)
告诉策略StoreEntry将被引用。在条目被锁定后调用。
node是指向存储条目的策略特定数据存储位置的指针,目前大小为一个(void *)指针。
🔗 policy.Dereferenced()
policy->Dereferenced(RemovalPolicy *policy, const StoreEntry *, RemovalPolicyNode *node)
告诉策略StoreEntry已被引用。在访问完条目后调用。
node是指向存储条目的策略特定数据存储位置的指针,目前大小为一个(void *)指针。
🔗 policy.WalkInit()
RemovalPolicyWalker walker = policy->WalkInit(RemovalPolicy *policy)
初始化策略实例中所有对象的遍历。对象以适合在重建策略时用作重新插入顺序的顺序返回。
返回的RemovalPolicyWalker实例是cbdata注册的。
注意:遍历必须作为原子操作执行,不能有其他策略操作介入,否则结果将是未定义的。
🔗 walker.Next()
const StoreEntry *entry = walker->Next(RemovalPolicyWalker *walker)
获取遍历链中的下一个对象。
当没有更多对象时返回NULL。
🔗 walker.Done()
walker->Done(RemovalPolicyWalker *walker)
完成已维护对象的遍历,销毁walker。
🔗 policy.PurgeInit()
RemovalPurgeWalker purgewalker = policy->PurgeInit(RemovalPolicy *policy, int max_scan)
初始化对删除候选对象的搜索。搜索深度由max_scan指示。
返回的RemovalPurgeWalker实例是cbdata注册的。
注意:遍历必须作为原子操作执行,不能有其他策略操作介入,否则结果将是未定义的。
🔗 purgewalker.Next()
StoreEntry *entry = purgewalker->Next(RemovalPurgeWalker *purgewalker)
获取下一个要清除的对象。purgewalker将从策略中删除每个返回的对象。
策略有责任验证对象未被锁定或以其他方式阻止被移除。这意味着策略不得返回storeEntryLocked()为true的对象。
当策略中没有更多可清除的对象时返回NULL。
🔗 purgewalker.Done()
purgewalker->Done(RemovalPurgeWalker *purgewalker)
完成已维护对象的遍历,销毁walker并恢复策略的正常状态。
🔗 policy.Stats()
purgewalker->Stats(RemovalPurgeWalker *purgewalker, StoreEntry *entry)
将策略的统计信息附加到给定的条目。
🔗 源代码布局
策略实现位于src/repl/<name>/,在该目录中执行make应生成一个对象归档src/repl/<name>.a,其中包含实现该策略的所有对象。
🔗 内部结构
🔗 RemovalPolicy
typedef struct _RemovalPolicy RemovalPolicy;
struct _RemovalPolicy {
char *_type;
void *_data;
void (*add)(RemovalPolicy *policy, StoreEntry *);
... /* see the API definition above */
};
_type成员主要用于调试和诊断目的,应该是指向策略名称的指针(创建时使用的名称相同)。
_data成员用于存储策略特定的信息。
🔗 RemovalPolicyWalker
typedef struct _RemovalPolicyWalker RemovalPolicyWalker;
struct _RemovalPolicyWalker {
RemovalPolicy *_policy;
void *_data;
StoreEntry *(*next)(RemovalPolicyWalker *);
... /* see the API definition above */
};
🔗 RemovalPolicyNode
typedef struct _RemovalPolicyNode RemovalPolicyNode;
struct _RemovalPolicyNode {
void *data;
};
存储条目的策略特定信息。目前只有一个指针的空间,但计划以后提供更多空间,以允许简单策略“内联”存储所有数据以节省内存。
🔗 策略注册
策略在Squid二进制文件中根据用户构建Squid时选择的策略自动注册。未来可能会扩展以支持可加载模块。所有注册的策略都可供希望使用的对象存储使用。
🔗 策略实例创建
每个策略都必须实现一个“创建/新”函数RemovalPolicy * createRemovalPolicy_<name>(char *arguments)。此函数创建策略实例并至少用其支持的API方法填充它。目前所有API调用都是强制性的,但策略实现必须确保在填充结构之前将其NULL填充,以保证未来的API兼容性。
它还应该用指向策略特定数据的指针填充_data成员。
🔗 Walker
创建walker时,策略至少用其支持的API方法填充它。目前所有API调用都是强制性的,但策略实现必须确保在填充结构之前将其NULL填充,以保证未来的API兼容性。
🔗 设计说明/错误
RemovalPolicyNode的设计不完整/不足。目的是将索引指针的位置抽象化,使策略实现能够同时处理磁盘和内存缓存,但遗憾的是,基于堆的策略的清除方法需要更新此项,并且在一般情况下,清除方法知道如何清除信息也是可取的。我认为共识是,目前将两者紧密耦合在同一个StoreEntry上的设计不是最佳设计。
关于让策略索引控制干净索引写入的设计是否正确,存在争议。也许不是。也许更合适的设计是完全在策略实现之外(即使用哈希索引)进行存储索引,并且只要求策略以某种方式转储其状态。
Referenced/Dereferenced()调用目前映射到lock/unlock,这是对其调用时间的近似。然而,真正的意图是当对象被引用时调用Referenced(),而仅在对象实际用于有益用途时才调用Dereferenced()。
🔗 转发选择
待续……
导航:网站搜索,网站页面,分类,🔼 向上