-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcacheus-tags.el
More file actions
143 lines (126 loc) · 5.8 KB
/
cacheus-tags.el
File metadata and controls
143 lines (126 loc) · 5.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
;;; cacheus-tags.el --- Core tag management utilities for Cacheus -*-
;;; lexical-binding: t; -*-
;;; Commentary:
;;
;; This file centralizes the core logic for managing tags associated with cache
;; entries. Tags provide a powerful mechanism for invalidating groups of
;; related entries without needing to know their specific keys.
;;
;; This module works in conjunction with `cacheus-eviction.el` and
;; `cacheus-storage.el` to ensure that tag indexes are kept consistent as
;; entries are added or removed from the cache.
;;; Code:
(require 'cl-lib)
(require 'ht)
(require 'dash)
(require 'cacheus-structs)
(require 'cacheus-util)
(declare-function cacheus-evict-one-entry "cacheus-eviction")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Tag Management (Package-Private)
(defun cacheus-update-entry-tag-info (ekey tags instance logger)
"Update all tag indexes for EKEY with TAGS.
This function maintains two data structures:
1. `entry-tags-ht`: Maps an entry key to its list of tags.
2. `tags-idx-ht`: An inverted index mapping a tag to a list of
keys that have that tag.
Arguments:
- `EKEY` (any): The effective key of the entry being tagged.
- `TAGS` (list): A list of tag symbols.
- `INSTANCE` (cacheus-instance): The live cache instance.
- `LOGGER` (function): The resolved logger function.
Returns:
None. Modifies the instance's runtime data in-place."
(let* ((data (cacheus-instance-runtime-data instance))
(et-ht (cacheus-runtime-data-entry-tags-ht data))
(tags-idx-ht (cacheus-runtime-data-tags-idx-ht data)))
(when (and tags et-ht tags-idx-ht)
(let ((unique-tags (-distinct tags)))
(when unique-tags
(puthash ekey unique-tags et-ht)
(-each unique-tags
(lambda (tag)
(let ((keys (gethash tag tags-idx-ht)))
(pushnew ekey keys)
(puthash tag keys tags-idx-ht)))))))))
(defun cacheus-remove-entry-tag-info (ekey instance logger)
"Remove all tag information for EKEY from the instance's tag indexes.
This is called during eviction to ensure the tag indexes remain consistent.
Arguments:
- `EKEY` (any): The effective key of the entry being evicted.
- `INSTANCE` (cacheus-instance): The live cache instance.
- `LOGGER` (function): The resolved logger function.
Returns:
None. Modifies the instance's runtime data in-place."
(let* ((name (cacheus-options-name (cacheus-instance-options instance)))
(data (cacheus-instance-runtime-data instance))
(et-ht (cacheus-runtime-data-entry-tags-ht data))
(tags-idx-ht (cacheus-runtime-data-tags-idx-ht data)))
(when (and et-ht tags-idx-ht)
(when-let ((tags (gethash ekey et-ht)))
(-each tags
(lambda (tag)
(when-let* ((keys (gethash tag tags-idx-ht))
(updated (remove ekey keys)))
(if (null updated)
(remhash tag tags-idx-ht)
(puthash tag updated tags-idx-ht))))))
(remhash ekey et-ht)
(funcall logger :debug "[C:%s] Removed tags for %S" name ekey)))))
(defun cacheus-get-keys-by-tags (tags-idx-ht tags all-must-match)
"Return a list of keys matching TAGS based on matching strategy."
(if all-must-match
;; Intersect the key lists for all specified tags.
(when-let ((initial-keys (gethash (car tags) tags-idx-ht)))
(-reduce 'intersection (cdr tags)
:initial-value initial-keys
:key (lambda (tag) (gethash tag tags-idx-ht))))
;; Union the key lists for all specified tags.
(let ((keys-ht (make-hash-table :test 'equal)))
(dolist (tag tags)
(dolist (k (gethash tag tags-idx-ht))
(puthash k t keys-ht)))
(hash-table-keys keys-ht))))
(cl-defun cacheus-invalidate-keys-by-tags
(instance tags &key (all-must-match nil) (run-hooks t))
"Find and evict cache entries based on a list of TAGS.
Arguments:
- `INSTANCE` (cacheus-instance): The live cache instance.
- `TAGS` (list): A list of tag symbols to match.
- `ALL-MUST-MATCH` (boolean): If non-nil, an entry must have all
tags to be invalidated. If nil, any entry with at least one
of the tags will be invalidated.
- `RUN-HOOKS` (boolean): If non-nil, run the `:expiration-hook` for
each invalidated entry.
Returns:
(list) A list of keys that were invalidated."
(let* ((opts (cacheus-instance-options instance))
(data (cacheus-instance-runtime-data instance))
(name (cacheus-options-name opts))
(logger (cacheus-resolve-logger (cacheus-options-logger opts)))
(hook (cacheus-options-expiration-hook opts))
(cache-ht (cacheus-runtime-data-cache-ht data))
(tags-idx-ht (cacheus-runtime-data-tags-idx-ht data))
(tags-to-match (if (listp tags) tags (list tags)))
keys)
(unless (seq-every-p #'symbolp tags-to-match)
(error "Tags must be symbols or a list of symbols. Got: %S" tags))
(setq keys (cacheus-get-keys-by-tags tags-idx-ht tags-to-match all-must-match))
(if keys
(progn
(funcall logger :info "[C:%s] Invalidating %d entries for tags %S"
name (length keys) tags-to-match)
(-each keys
(lambda (key)
(let ((entry (gethash key cache-ht)))
(cacheus-evict-one-entry key instance logger)
(when (and run-hooks hook entry)
(condition-case e (funcall hook key entry)
(error (funcall logger :error
"[C:%s] Hook error for key %S: %S"
name key e))))))))
(funcall logger :info "[C:%s] No entries found for tags %S"
name tags-to-match))
keys))
(provide 'cacheus-tags)
;;; cacheus-tags.el ends here