Store *key* -> *value* in the cache. *value* may be ``None`` (set/presence mode), a ``str``, or ``bytes``. Returns ``True`` on success, ``False`` on failure. If the key already exists and the new value fits in the existing heap region, the heap bytes are ov
(self, key, value=None)
| 1245 | # ------------------------------------------------------------------ |
| 1246 | |
| 1247 | def put(self, key, value=None): |
| 1248 | """ |
| 1249 | Store *key* -> *value* in the cache. |
| 1250 | |
| 1251 | *value* may be ``None`` (set/presence mode), a ``str``, or ``bytes``. |
| 1252 | Returns ``True`` on success, ``False`` on failure. |
| 1253 | |
| 1254 | If the key already exists and the new value fits in the existing heap |
| 1255 | region, the heap bytes are overwritten in-place. Otherwise the new |
| 1256 | value is appended to the heap and the index pointer is updated. |
| 1257 | |
| 1258 | Thread-safe: acquires ``_thread_lock`` (RLock) then the cross-process |
| 1259 | ``fcntl.flock`` **before** ``open(write=True)`` maps the index. Mapping |
| 1260 | prior to the flock would let another writer mutate the file while this |
| 1261 | process holds a stale mmap (multiprocess data loss). |
| 1262 | """ |
| 1263 | key_bytes = salt.utils.stringutils.to_bytes(key)[: self.key_size] |
| 1264 | if value is None: |
| 1265 | val_bytes = b"" |
| 1266 | elif isinstance(value, bytes): |
| 1267 | val_bytes = value |
| 1268 | else: |
| 1269 | val_bytes = salt.utils.stringutils.to_bytes(value) |
| 1270 | |
| 1271 | mtime_ns = time.time_ns() |
| 1272 | |
| 1273 | try: |
| 1274 | with self._thread_lock: |
| 1275 | with self._lock(): |
| 1276 | if not self.open(write=True): |
| 1277 | return False |
| 1278 | slot, found = self._find_slot(key_bytes) |
| 1279 | if slot is None: |
| 1280 | log.error("Mmap cache index is full!") |
| 1281 | return False |
| 1282 | |
| 1283 | s_offset = slot * self.slot_size |
| 1284 | |
| 1285 | if found: |
| 1286 | existing_heap_off, existing_len, _ = self._read_slot_pointer( |
| 1287 | s_offset |
| 1288 | ) |
| 1289 | if len(val_bytes) <= existing_len: |
| 1290 | # In-place overwrite: hand the actual value |
| 1291 | # bytes to ``_overwrite_in_heap`` (no NUL |
| 1292 | # padding). The slot's LENGTH field becomes |
| 1293 | # authoritative for reads; trailing bytes |
| 1294 | # from the previous, larger value remain in |
| 1295 | # the heap as unreferenced garbage that |
| 1296 | # ``atomic_rebuild`` reclaims. Padding here |
| 1297 | # plus a rstrip CRC inside _overwrite_in_heap |
| 1298 | # corrupted binary values whose final byte |
| 1299 | # was NUL — see BUG.md. |
| 1300 | if not self._overwrite_in_heap( |
| 1301 | existing_heap_off, val_bytes |
| 1302 | ): |
| 1303 | return False |
| 1304 | struct.pack_into( |