diff --git a/noxfile.py b/noxfile.py index 2af3053..342ef68 100644 --- a/noxfile.py +++ b/noxfile.py @@ -6,7 +6,7 @@ nox.options.sessions = "lint", "types", "tests" locations = "src", "tests", "noxfile.py" DEFAULT_VERSION = "3.8" -DEFAULT_BENCHMARK_VERSION = "3.11" +DEFAULT_BENCHMARK_VERSIONS = ["3.11"] VERSIONS = ["3.8", "3.11"] @@ -57,7 +57,7 @@ def tests(session: Session) -> None: session.run("pytest", *args, env={"PYTHONHASHSEED": "0"}) -@session(python=DEFAULT_BENCHMARK_VERSION) +@session(python=DEFAULT_BENCHMARK_VERSIONS) def benchmark(session: Session) -> None: """Run the benchmark suite.""" args = session.posargs diff --git a/poetry.lock b/poetry.lock index b0467ac..f4d4ceb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,56 +1,12 @@ +# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. + [[package]] name = "marisa-trie" version = "1.0.0" description = "Static memory-efficient and fast Trie-like structures for Python." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -setuptools = "*" - -[package.extras] -test = ["hypothesis", "pytest", "readme-renderer"] - -[[package]] -name = "prometheus-client" -version = "0.17.1" -description = "Python client for the Prometheus monitoring system." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -twisted = ["twisted"] - -[[package]] -name = "setuptools" -version = "68.2.2" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" -optional = false -python-versions = ">=3.8" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "uhashring" -version = "2.3" -description = "Full featured consistent hashing python library compatible with ketama." -category = "main" optional = false python-versions = ">=3.7" - -[metadata] -lock-version = "1.1" -python-versions = "^3.8" -content-hash = "5ae5733989516d8828c4128c938074520f1edc772e205466fb5ae96bb0eb2864" - -[metadata.files] -marisa-trie = [ +files = [ {file = "marisa-trie-1.0.0.tar.gz", hash = "sha256:d8a68301f023a724eb379aaa1b10f88a9e1a458cc8a41b527c62507859b4a4d2"}, {file = "marisa_trie-1.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ac817d167f84bdc4de2a737fb5ffb635ba1a3fdf3dbbcc06541b2cb420416e79"}, {file = "marisa_trie-1.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6bc75721f4fb8c1d1d0812d2ed17e57ff77e7192c792114af941c12dab1a4346"}, @@ -112,15 +68,113 @@ marisa-trie = [ {file = "marisa_trie-1.0.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34ec0f3bb54479a49dd60bb712fee851e7c135504d84efcf030815a07a9a96f5"}, {file = "marisa_trie-1.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:eba700d6f24906dff4e593be1af93ebf38c841b0755ee701c816674f133dee29"}, ] -prometheus-client = [ + +[package.dependencies] +setuptools = "*" + +[package.extras] +test = ["hypothesis", "pytest", "readme-renderer"] + +[[package]] +name = "meta-memcache-socket" +version = "0.1.0" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "meta_memcache_socket-0.1.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:276ff6496b1bd2697c01bc7532bf212fd7bed53bca7f9860b097fefd44f0f255"}, + {file = "meta_memcache_socket-0.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:85c877cdd9568377111ac8e45c3b1d3ac88fd3c7d1dbce769bc598e2917a8d13"}, + {file = "meta_memcache_socket-0.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50939f7225c0a1849fab1ba08441f5dfaea66e08520fbc79725df5da75ea11f9"}, + {file = "meta_memcache_socket-0.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13783808b431c149e0f4de7bdd631df6004a971d1717189b93796b63835fe108"}, + {file = "meta_memcache_socket-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8a8c2e62c7afdb4604a84638b17074266e5f285ca0fc9524af1ab81e13ea520"}, + {file = "meta_memcache_socket-0.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:77031dfd683856d9f3e5461d3b5234b6df008d45dbc6a85d0b9a94b55db2d7c8"}, + {file = "meta_memcache_socket-0.1.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:9a771144b6db23570fdf0f1008aa6017e65464e720e05038c8479a8146e2be19"}, + {file = "meta_memcache_socket-0.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4b262cb9aa55424e840385c43af84b22bb16d0f29e8e15ce1ddf6afcf567b5bc"}, + {file = "meta_memcache_socket-0.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7429efaace51c28adc876f16994930d9c1a0507d1a3c4b2e0ee0a7070f22eba8"}, + {file = "meta_memcache_socket-0.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:976cd618023d78169b41b9cd9ae3bccccd185205241b5146a3e8859edb458f27"}, + {file = "meta_memcache_socket-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc3c87c581ff04902bce2a08c67e646142f33da4d0a2460d74ab0c2701a7cc09"}, + {file = "meta_memcache_socket-0.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:98efeca606829f4f5bd7add7b1c698a3a6da2565df2d239295d162484f334e04"}, + {file = "meta_memcache_socket-0.1.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:fe050b98fcdef5c8b1c0c993f69397220b0a605884c2439be6fd4cb8fd4274f0"}, + {file = "meta_memcache_socket-0.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b2b93aa16620c224735d162e2cb9b5623a3c46c43a7a33f4c1c6fcd1df1d55e"}, + {file = "meta_memcache_socket-0.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77bb692a2522c70ca57bd393eb664148957982a607b1bc07cc18d4c733c504d5"}, + {file = "meta_memcache_socket-0.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:55f92f2bd27a3bb96fb0d1e0ab34196ff3929f84656852a5ddaac578fbf7c1c8"}, + {file = "meta_memcache_socket-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:682007ae78f1eaeaa3eaabb4db956d4b6950d34737dd4e0cd99c1b0efb477b14"}, + {file = "meta_memcache_socket-0.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:39e7b14f54c634861a13befb5a753991f8535378237c0680f0445cbbf1a72509"}, + {file = "meta_memcache_socket-0.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1c25df35051ecdae27a6052d4d533f3e8290bf49ca7f51f6960691d990b9800"}, + {file = "meta_memcache_socket-0.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:815fd108ab5b3dfcf974315c88d799eff26801203d50d1cc31a1d387610626ce"}, + {file = "meta_memcache_socket-0.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f22f9dfcee14cfcc508d235e4862721a88d8167e75b51c1679876a807b2fa3cc"}, + {file = "meta_memcache_socket-0.1.0-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:89a197c4f9b62baf0ceede51e678d86a939083500f8d648315626f11d9fc19f4"}, + {file = "meta_memcache_socket-0.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e7e35b06f06292db5edcb542e14a3b875c215839e49dc3dd061dadcba6285d5"}, + {file = "meta_memcache_socket-0.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5741db35da60f514321430a24cafb8a45fd3c6253e19f193175446d1c71a66f8"}, + {file = "meta_memcache_socket-0.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3150319f6873ec8a65d523fc8da9fe2ee99154a2bc73552a8446510e556a500"}, + {file = "meta_memcache_socket-0.1.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bef08e5527df2e287e89bc04dda9fce554ddb7e782b497ab97b735ebca653792"}, + {file = "meta_memcache_socket-0.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0bd85fca14237950f850a7bfaaa9cdc09f7e63bc1f453eed58c5921cdfaaec46"}, + {file = "meta_memcache_socket-0.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f47eaa393d0cc5c4c2b4b87ee433698cda353e1d60107315891293bf7ef4eff0"}, + {file = "meta_memcache_socket-0.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:876767acbc5ae7ff7494bcbb2488b260928e0016bc73906bc94ce5be047fe974"}, + {file = "meta_memcache_socket-0.1.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbc18c52f844a0eea17037718931c44f79597d2a594c9cff58c0664e76df85b"}, + {file = "meta_memcache_socket-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50ba38faabaefe49b39c57e2abca30ffb060b6aaefb928fae9a940fa74a69856"}, + {file = "meta_memcache_socket-0.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d07f0f8e18f155c9d97a667a084d026aa16db681be020a7dad8d3443da073225"}, + {file = "meta_memcache_socket-0.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4652f1455e0478ee825ca6828b368f60f9acd582c2d94298e624cf2804bbaac3"}, + {file = "meta_memcache_socket-0.1.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91b8aad8bfcb327f440cc6fbdc893aac20a36638b88fdd494ab1e36776455977"}, + {file = "meta_memcache_socket-0.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0b338a9b0b07823d1653ab4a2f6d1d7db5e70416968e64cc070fd983d7e47c4"}, + {file = "meta_memcache_socket-0.1.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4de8135df114d97e99fa4171a10504812aaab2405e1ffb9987c4aa26ee11569b"}, + {file = "meta_memcache_socket-0.1.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f9baa601dba6cc9f6838472c9f8acbb4226a8279886e237c2f1a02ca752d558"}, + {file = "meta_memcache_socket-0.1.0-pp37-pypy37_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8fdf92bf7eec9b340c49f8059a3032cd5cf70dbf42eca1f5db65d5e51dc6ad17"}, + {file = "meta_memcache_socket-0.1.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4026106469d5fc707c5920b7ef98c5faad0fd8c9c2411c9cf169e629d433f7a5"}, + {file = "meta_memcache_socket-0.1.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5e56237cb45343135d4e78aaed1a6045d8f7a95beaba2a836a423c0dfa6a5799"}, + {file = "meta_memcache_socket-0.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d6e97e1ac7133bf3514c122aebd443754c7ac8138c27b48a4470e260b9092f4"}, + {file = "meta_memcache_socket-0.1.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e8766dbe2ebd9b9abe536eb62648e8edbff5c9e79bb60f7bd42bc8f09dca8825"}, + {file = "meta_memcache_socket-0.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d835e0b69c357ae83d17fd078efc0e464a62397ae732ca58e169381284311bfb"}, + {file = "meta_memcache_socket-0.1.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c10ccb6f62c90ab1bb6345ecf504e1d5d8450cd7a81be8d15322c1eff0c0e0e7"}, + {file = "meta_memcache_socket-0.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8164586cbc9ac16376945f14c261b49b8905f2d5b3ef2207cffe90a1f0ca86db"}, + {file = "meta_memcache_socket-0.1.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd661f503ecc9ae8020bca79fcfb814f710df2e91cf98086636d5ae9e7689ef0"}, + {file = "meta_memcache_socket-0.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ab910371da3d13abd366d1fb36e0c961b5fbad85c257265bb18603dbd52d0d3"}, + {file = "meta_memcache_socket-0.1.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b69fbb37d392d1cbae85deabc4efd475eea5867b6b45469add3300e21115880"}, + {file = "meta_memcache_socket-0.1.0.tar.gz", hash = "sha256:244a53055c069dd232dd8733c93a494ce4e437a5bfa2fb14ef7ec8f51c11d76d"}, +] + +[[package]] +name = "prometheus-client" +version = "0.17.1" +description = "Python client for the Prometheus monitoring system." +optional = false +python-versions = ">=3.6" +files = [ {file = "prometheus_client-0.17.1-py3-none-any.whl", hash = "sha256:e537f37160f6807b8202a6fc4764cdd19bac5480ddd3e0d463c3002b34462101"}, {file = "prometheus_client-0.17.1.tar.gz", hash = "sha256:21e674f39831ae3f8acde238afd9a27a37d0d2fb5a28ea094f0ce25d2cbf2091"}, ] -setuptools = [ + +[package.extras] +twisted = ["twisted"] + +[[package]] +name = "setuptools" +version = "68.2.2" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, ] -uhashring = [ + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "uhashring" +version = "2.3" +description = "Full featured consistent hashing python library compatible with ketama." +optional = false +python-versions = ">=3.7" +files = [ {file = "uhashring-2.3-py3-none-any.whl", hash = "sha256:7ee8a25ca495a97effad10bd563c83b4054a6d7606d9530757049a04edab9297"}, {file = "uhashring-2.3.tar.gz", hash = "sha256:9f76187e8d8e82f6e5519c995eef1f1bf44d4a5e0fc4fdd1219a044b10040612"}, ] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "ce30e2f682597672f0fc2ef59821c0be70841bfd087134b66b86c1f7580d7d25" diff --git a/pyproject.toml b/pyproject.toml index 13d0f1e..84c918c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ packages = [{include = "meta_memcache", from="src"}] python = "^3.8" uhashring = "^2.1" marisa-trie = "^1.0.0" +meta-memcache-socket = "^0.1.0" [tool.poetry.group.extras.dependencies] prometheus-client = "^0.17.1" diff --git a/src/meta_memcache/__init__.py b/src/meta_memcache/__init__.py index d0f4391..6dee464 100644 --- a/src/meta_memcache/__init__.py +++ b/src/meta_memcache/__init__.py @@ -40,6 +40,7 @@ Miss, NotStored, ServerVersion, + ResponseFlags, SetMode, Success, TokenFlag, diff --git a/src/meta_memcache/commands/high_level_commands.py b/src/meta_memcache/commands/high_level_commands.py index 582b6be..356bf1d 100644 --- a/src/meta_memcache/commands/high_level_commands.py +++ b/src/meta_memcache/commands/high_level_commands.py @@ -287,23 +287,23 @@ def get_or_lease_cas( if isinstance(result, Value): # It is a hit. - if result.win: + if result.flags.win: # Win flag present, meaning we got the lease to # recache/cache the item. We need to mimic a miss. - return None, result.cas_token - if result.size == 0 and result.win is False: + return None, result.flags.cas_token + if result.size == 0 and result.flags.win is False: # The value is empty, this is a miss lease, # and we lost, so we must keep retrying and - # wait for the winner to populate the value. + # wait for the.flags.winner to populate the value. if i < lease_policy.miss_retries: continue else: # We run out of retries, behave as a miss - return None, result.cas_token + return None, result.flags.cas_token else: # There is data, either the is no lease or # we lost and should use the stale value. - return result.value, result.cas_token + return result.value, result.flags.cas_token else: # With MISS_LEASE_TTL we should always get a value # because on miss a lease empty value is generated @@ -378,7 +378,7 @@ def get_cas( if result is None: return None, None else: - return result.value, result.cas_token + return result.value, result.flags.cas_token def _get( self: HighLevelCommandMixinWithMetaCommands, @@ -414,7 +414,7 @@ def _process_get_result( ) -> Optional[Value]: if isinstance(result, Value): # It is a hit - if result.win: + if result.flags.win: # Win flag present, meaning we got the lease to # recache the item. We need to mimic a miss, so # we set the value to None. diff --git a/src/meta_memcache/connection/memcache_socket.py b/src/meta_memcache/connection/memcache_socket.py index 4c1d967..5240347 100644 --- a/src/meta_memcache/connection/memcache_socket.py +++ b/src/meta_memcache/connection/memcache_socket.py @@ -1,17 +1,21 @@ import logging import socket -from typing import Union +from typing import Optional, Tuple, Union + +import meta_memcache_socket from meta_memcache.errors import MemcacheError from meta_memcache.protocol import ( ENDL, ENDL_LEN, + EMPTY_RESPONSE_FLAGS, NOOP, Conflict, Miss, NotStored, ServerVersion, Success, + ResponseFlags, Value, get_store_success_response_header, ) @@ -119,7 +123,9 @@ def _reset_buffer(self) -> None: self._pos = 0 self._read = remaining_data - def _get_single_header(self) -> memoryview: + def _get_single_header( + self, + ) -> Tuple[int, Optional[int], Optional[int], Optional[ResponseFlags]]: # Reset buffer for new data if self._read == self._pos: self._read = 0 @@ -127,22 +133,19 @@ def _get_single_header(self) -> memoryview: elif self._pos > self._reset_buffer_size: self._reset_buffer() - endl_pos = -1 while True: - if self._read - self._pos > ENDL_LEN: - endl_pos = self._buf.find(ENDL, self._pos, self._read) - if endl_pos >= 0: - break + if self._read != self._pos: + # We have data in the buffer: find the header + if header_data := meta_memcache_socket.parse_header( + self._buf_view, self._pos, self._read + ): + self._pos = header_data[0] + return header_data # Missing data, but still space in buffer, so read more if self._recv_info_buffer() <= 0: break - if endl_pos < 0: - raise MemcacheError("Bad response. Socket might have closed unexpectedly") - - pos = self._pos - self._pos = endl_pos + ENDL_LEN - return self._buf_view[pos:endl_pos] + raise MemcacheError("Bad response. Socket might have closed unexpectedly") def sendall(self, data: bytes, with_noop: bool = False) -> None: if with_noop: @@ -153,10 +156,12 @@ def sendall(self, data: bytes, with_noop: bool = False) -> None: def _read_until_noop_header(self) -> None: while self._noop_expected > 0: header = self._get_single_header() - if header[0:2] == b"MN": + if header[1] == meta_memcache_socket.RESPONSE_NOOP: self._noop_expected -= 1 - def _get_header(self) -> memoryview: + def _get_header( + self, + ) -> Tuple[int, Optional[int], Optional[int], Optional[ResponseFlags]]: try: if self._noop_expected > 0: self._read_until_noop_header() @@ -169,30 +174,36 @@ def _get_header(self) -> memoryview: def get_response( self, ) -> Union[Value, Success, NotStored, Conflict, Miss]: - header = self._get_header().tobytes() - response_code = header[0:2] + (_, response_code, size, flags) = self._get_header() result: Union[Value, Success, NotStored, Conflict, Miss] try: - if response_code == b"VA": + if response_code == meta_memcache_socket.RESPONSE_VALUE: + if size is None: + raise MemcacheError("Bad value response. Missing size") # Value response - result = Value.from_header(header) - elif response_code == self._store_success_response_header: + result = Value( + size=size, flags=flags or EMPTY_RESPONSE_FLAGS, value=None + ) + elif response_code == meta_memcache_socket.RESPONSE_SUCCESS: # Stored or no value, return Success - result = Success.from_header(header) - elif response_code == b"NS": + result = Success(flags=flags or EMPTY_RESPONSE_FLAGS) + elif response_code == meta_memcache_socket.RESPONSE_NOT_STORED: # Value response, parse size and flags result = NOT_STORED - elif response_code == b"EX": + elif response_code == meta_memcache_socket.RESPONSE_CONFLICT: # Already exists, not changed, CAS conflict result = CONFLICT - elif response_code == b"EN" or response_code == b"NF": + elif response_code == meta_memcache_socket.RESPONSE_MISS: # Not Found, Miss. result = MISS else: - raise MemcacheError(f"Unknown response: {bytes(response_code)!r}") + raise MemcacheError(f"Unknown response: {response_code}") except Exception as e: - _log.warning(f"Error parsing response header in {self}: {header!r}") - raise MemcacheError(f"Error parsing response header {header!r}") from e + _log.warning( + f"Error parsing response header in {self}: " + f"Response: {response_code}, size {size}, flags: {flags}" + ) + raise MemcacheError("Error parsing response header") from e return result diff --git a/src/meta_memcache/executors/default.py b/src/meta_memcache/executors/default.py index 6e382ae..0383aa6 100644 --- a/src/meta_memcache/executors/default.py +++ b/src/meta_memcache/executors/default.py @@ -17,6 +17,7 @@ MetaCommand, Miss, NotStored, + ResponseFlags, ServerVersion, Success, TokenFlag, @@ -252,12 +253,12 @@ def _conn_recv_response( Read response on a connection """ if flags and Flag.NOREPLY in flags: - return Success() + return Success(flags=ResponseFlags()) result = conn.get_response() if isinstance(result, Value): data = conn.get_value(result.size) if result.size > 0: - encoding_id = result.client_flag or 0 + encoding_id = result.flags.client_flag or 0 try: result.value = self._serializer.unserialize(data, encoding_id) except Exception: diff --git a/src/meta_memcache/extras/migrating_cache_client.py b/src/meta_memcache/extras/migrating_cache_client.py index dac181f..664ffd4 100644 --- a/src/meta_memcache/extras/migrating_cache_client.py +++ b/src/meta_memcache/extras/migrating_cache_client.py @@ -78,7 +78,11 @@ def get_migration_mode(self) -> MigrationMode: return current_mode def _get_value_ttl(self, value: Value) -> int: - ttl = value.ttl if value.ttl is not None else self._default_read_backfill_ttl + ttl = ( + value.flags.ttl + if value.flags.ttl is not None + else self._default_read_backfill_ttl + ) if ttl < 0: # TTL for items marked to store forvered is returned as -1 ttl = 0 diff --git a/src/meta_memcache/extras/probabilistic_hot_cache.py b/src/meta_memcache/extras/probabilistic_hot_cache.py index 1e69699..3766a85 100644 --- a/src/meta_memcache/extras/probabilistic_hot_cache.py +++ b/src/meta_memcache/extras/probabilistic_hot_cache.py @@ -124,10 +124,11 @@ def _store_in_hot_cache_if_necessary( allowed: bool, ) -> None: if not is_hot: - hit_after_write = value.fetched or 0 - last_read_age = value.last_access if value.last_access is not None else 9999 + last_read_age = ( + value.flags.last_access if value.flags.last_access is not None else 9999 + ) if ( - hit_after_write > 0 + value.flags.fetched and last_read_age <= self._max_last_access_age_seconds ): # Is detected as hot diff --git a/src/meta_memcache/protocol.py b/src/meta_memcache/protocol.py index 95216d0..37302db 100644 --- a/src/meta_memcache/protocol.py +++ b/src/meta_memcache/protocol.py @@ -2,6 +2,8 @@ from enum import Enum, IntEnum from typing import Any, Dict, List, Optional, Union +from meta_memcache_socket import ResponseFlags + ENDL = b"\r\n" NOOP: bytes = b"mn" + ENDL ENDL_LEN = 2 @@ -103,168 +105,25 @@ class Miss(MemcacheResponse): # Response flags -TOKEN_FLAG_OPAQUE = ord("O") -INT_FLAG_CAS_TOKEN = ord("c") -INT_FLAG_FETCHED = ord("h") -INT_FLAG_LAST_ACCESS = ord("l") -INT_FLAG_TTL = ord("t") -INT_FLAG_CLIENT_FLAG = ord("f") -INT_FLAG_SIZE = ord("s") -FLAG_WIN = ord("W") -FLAG_LOST = ord("Z") -FLAG_STALE = ord("X") - - -# @dataclass(slots=True, init=False) +EMPTY_RESPONSE_FLAGS = ResponseFlags() + + @dataclass class Success(MemcacheResponse): - __slots__ = ( - "cas_token", - "fetched", - "last_access", - "ttl", - "client_flag", - "win", - "stale", - "real_size", - "opaque", - ) - cas_token: Optional[int] - fetched: Optional[int] - last_access: Optional[int] - ttl: Optional[int] - client_flag: Optional[int] - win: Optional[bool] - stale: bool - real_size: Optional[int] - opaque: Optional[bytes] - - def __init__( - self, - *, - cas_token: Optional[int] = None, - fetched: Optional[int] = None, - last_access: Optional[int] = None, - ttl: Optional[int] = None, - client_flag: Optional[int] = None, - win: Optional[bool] = None, - stale: bool = False, - real_size: Optional[int] = None, - opaque: Optional[bytes] = None, - ) -> None: - self.cas_token = cas_token - self.fetched = fetched - self.last_access = last_access - self.ttl = ttl - self.client_flag = client_flag - self.win = win - self.stale = stale - self.real_size = real_size - self.opaque = opaque + __slots__ = ("flags",) + flags: ResponseFlags @classmethod - def from_header(cls, header: "Blob") -> "Success": - result = cls() - result._set_flags(header) - return result - - def _set_flags(self, header: bytes, pos: int = 3) -> None: # noqa: C901 - header_size = len(header) - while pos < header_size: - flag = header[pos] - pos += 1 - if flag == SPACE: - continue - end = pos - while end < header_size: - if header[end] == SPACE: - break - end += 1 - - if flag == INT_FLAG_CAS_TOKEN: - self.cas_token = int(header[pos:end]) - elif flag == INT_FLAG_FETCHED: - self.fetched = int(header[pos:end]) - elif flag == INT_FLAG_LAST_ACCESS: - self.last_access = int(header[pos:end]) - elif flag == INT_FLAG_TTL: - self.ttl = int(header[pos:end]) - elif flag == INT_FLAG_CLIENT_FLAG: - self.client_flag = int(header[pos:end]) - elif flag == FLAG_WIN: - self.win = True - elif flag == FLAG_LOST: - self.win = False - elif flag == FLAG_STALE: - self.stale = True - elif flag == INT_FLAG_SIZE: - self.real_size = int(header[pos:end]) - elif flag == TOKEN_FLAG_OPAQUE: - self.opaque = header[pos:end] - pos = end + 1 - - -# @dataclass(slots=True, init=False) + def default(cls) -> "Success": + return cls(flags=ResponseFlags()) + + @dataclass class Value(Success): - __slots__ = ( - "cas_token", - "fetched", - "last_access", - "ttl", - "client_flag", - "win", - "stale", - "real_size", - "opaque", - "size", - "value", - ) + __slots__ = ("flags", "size", "value") size: int value: Optional[Any] - def __init__( - self, - *, - size: int, - value: Optional[Any] = None, - cas_token: Optional[int] = None, - fetched: Optional[int] = None, - last_access: Optional[int] = None, - ttl: Optional[int] = None, - client_flag: Optional[int] = None, - win: Optional[bool] = None, - stale: bool = False, - real_size: Optional[int] = None, - opaque: Optional[bytes] = None, - ) -> None: - self.size = size - self.value = value - self.cas_token = cas_token - self.fetched = fetched - self.last_access = last_access - self.ttl = ttl - self.client_flag = client_flag - self.win = win - self.stale = stale - self.real_size = real_size - self.opaque = opaque - - @classmethod - def from_header(cls, header: "Blob") -> "Value": - header_size = len(header) - if header_size < 4 or header[2] != SPACE: - raise ValueError(f"Invalid header {header!r}") - end = 4 - while end < header_size: - if header[end] == SPACE: - break - end += 1 - size = int(header[3:end]) - result = cls(size=size) - result._set_flags(header, pos=end + 1) - return result - @dataclass class ValueContainer: diff --git a/tests/commands_test.py b/tests/commands_test.py index c48d791..3bd2241 100644 --- a/tests/commands_test.py +++ b/tests/commands_test.py @@ -27,6 +27,7 @@ from meta_memcache.protocol import ( Miss, NotStored, + ResponseFlags, ServerVersion, Success, IntFlag, @@ -133,7 +134,7 @@ def test_set_cmd( memcache_socket: MemcacheSocket, cache_client: CacheClient, ) -> None: - memcache_socket.get_response.return_value = Success() + memcache_socket.get_response.return_value = Success(flags=ResponseFlags()) cache_client.set(key="foo", value="bar", ttl=300) memcache_socket.sendall.assert_called_once_with( @@ -277,7 +278,7 @@ def test_set_cmd_1_6_6( memcache_socket_1_6_6: MemcacheSocket, cache_client_1_6_6: CacheClient, ) -> None: - memcache_socket_1_6_6.get_response.return_value = Success() + memcache_socket_1_6_6.get_response.return_value = Success(flags=ResponseFlags()) cache_client_1_6_6.set(key="foo", value="bar", ttl=300) memcache_socket_1_6_6.sendall.assert_called_once_with( @@ -290,7 +291,7 @@ def test_set_success_fail( memcache_socket: MemcacheSocket, cache_client: CacheClient, ) -> None: - memcache_socket.get_response.return_value = Success() + memcache_socket.get_response.return_value = Success(flags=ResponseFlags()) result = cache_client.set(key=Key("foo"), value="bar", ttl=300) assert result is True @@ -302,7 +303,7 @@ def test_set_success_fail( def test_refill( cache_client_with_mocked_meta_commands: CacheApi, meta_command_mock: MagicMock ) -> None: - meta_command_mock.meta_set.return_value = Success() + meta_command_mock.meta_set.return_value = Success(flags=ResponseFlags()) cache_client_with_mocked_meta_commands.refill(key="foo", value="bar", ttl=300) meta_command_mock.meta_set.assert_called_once_with( key=Key(key="foo"), @@ -319,7 +320,7 @@ def test_delete_cmd( memcache_socket: MemcacheSocket, cache_client: CacheClient, ) -> None: - memcache_socket.get_response.return_value = Success() + memcache_socket.get_response.return_value = Success(flags=ResponseFlags()) cache_client.delete(key="foo") memcache_socket.sendall.assert_called_once_with(b"md foo\r\n", with_noop=False) @@ -367,7 +368,7 @@ def test_delete_success_fail( memcache_socket: MemcacheSocket, cache_client: CacheClient, ) -> None: - memcache_socket.get_response.return_value = Success() + memcache_socket.get_response.return_value = Success(flags=ResponseFlags()) result = cache_client.delete(key=Key("foo")) assert result is True @@ -384,7 +385,7 @@ def test_invalidate_cmd( memcache_socket: MemcacheSocket, cache_client: CacheClient, ) -> None: - memcache_socket.get_response.return_value = Success() + memcache_socket.get_response.return_value = Success(flags=ResponseFlags()) cache_client.invalidate(key="foo") memcache_socket.sendall.assert_called_once_with(b"md foo\r\n", with_noop=False) @@ -432,7 +433,7 @@ def test_invalidate_success_fail( memcache_socket: MemcacheSocket, cache_client: CacheClient, ) -> None: - memcache_socket.get_response.return_value = Success() + memcache_socket.get_response.return_value = Success(flags=ResponseFlags()) result = cache_client.invalidate(key=Key("foo")) assert result is True @@ -449,7 +450,7 @@ def test_touch_cmd( memcache_socket: MemcacheSocket, cache_client: CacheClient, ) -> None: - memcache_socket.get_response.return_value = Success() + memcache_socket.get_response.return_value = Success(flags=ResponseFlags()) cache_client.touch(key="foo", ttl=60) memcache_socket.sendall.assert_called_once_with(b"mg foo T60\r\n", with_noop=False) @@ -564,7 +565,9 @@ def test_get_cmd(memcache_socket: MemcacheSocket, cache_client: CacheClient) -> ) memcache_socket.sendall.reset_mock() - memcache_socket.get_response.return_value = Value(size=0, value=None) + memcache_socket.get_response.return_value = Value( + size=0, value=None, flags=ResponseFlags() + ) cache_client.get_or_lease( key=Key("foo"), lease_policy=LeasePolicy(), @@ -618,8 +621,10 @@ def test_get_value(memcache_socket: MemcacheSocket, cache_client: CacheClient) - memcache_socket.get_response.return_value = Value( size=len(encoded_value.data), value=None, - cas_token=expected_cas_token, - client_flag=encoded_value.encoding_id, + flags=ResponseFlags( + cas_token=expected_cas_token, + client_flag=encoded_value.encoding_id, + ), ) memcache_socket.get_value.return_value = encoded_value.data @@ -642,7 +647,7 @@ def test_get_value(memcache_socket: MemcacheSocket, cache_client: CacheClient) - def test_get_other(memcache_socket: MemcacheSocket, cache_client: CacheClient) -> None: - memcache_socket.get_response.return_value = Success() + memcache_socket.get_response.return_value = Success(flags=ResponseFlags()) try: cache_client.get( key=Key("foo"), @@ -661,8 +666,10 @@ def test_value_wrong_type( memcache_socket.get_response.return_value = Value( size=len(encoded_value.data), value=None, - cas_token=expected_cas_token, - client_flag=encoded_value.encoding_id, + flags=ResponseFlags( + cas_token=expected_cas_token, + client_flag=encoded_value.encoding_id, + ), ) memcache_socket.get_value.return_value = encoded_value.data @@ -695,8 +702,10 @@ def test_deserialization_error( memcache_socket.get_response.return_value = Value( size=len(encoded_value.data), value=None, - cas_token=expected_cas_token, - client_flag=encoded_value.encoding_id, + flags=ResponseFlags( + cas_token=expected_cas_token, + client_flag=encoded_value.encoding_id, + ), ) memcache_socket.get_value.return_value = encoded_value.data @@ -720,10 +729,12 @@ def test_recache_win_returns_miss( memcache_socket.get_response.return_value = Value( size=len(encoded_value.data), value=None, - win=True, - stale=True, - cas_token=expected_cas_token, - client_flag=encoded_value.encoding_id, + flags=ResponseFlags( + win=True, + stale=True, + cas_token=expected_cas_token, + client_flag=encoded_value.encoding_id, + ), ) memcache_socket.get_value.return_value = encoded_value.data @@ -741,10 +752,12 @@ def test_recache_lost_returns_stale_value( memcache_socket.get_response.return_value = Value( size=len(encoded_value.data), value=None, - win=False, - stale=True, - cas_token=expected_cas_token, - client_flag=encoded_value.encoding_id, + flags=ResponseFlags( + win=False, + stale=True, + cas_token=expected_cas_token, + client_flag=encoded_value.encoding_id, + ), ) memcache_socket.get_value.return_value = encoded_value.data @@ -762,8 +775,10 @@ def test_get_or_lease_hit( memcache_socket.get_response.return_value = Value( size=len(encoded_value.data), value=None, - cas_token=expected_cas_token, - client_flag=encoded_value.encoding_id, + flags=ResponseFlags( + cas_token=expected_cas_token, + client_flag=encoded_value.encoding_id, + ), ) memcache_socket.get_value.return_value = encoded_value.data @@ -786,8 +801,10 @@ def test_get_or_lease_miss_win( memcache_socket.get_response.return_value = Value( size=0, value=None, - win=True, - cas_token=expected_cas_token, + flags=ResponseFlags( + win=True, + cas_token=expected_cas_token, + ), ) memcache_socket.get_value.return_value = b"" @@ -813,20 +830,26 @@ def test_get_or_lease_miss_lost_then_data( Value( size=0, value=None, - win=False, - cas_token=expected_cas_token - 1, + flags=ResponseFlags( + win=False, + cas_token=expected_cas_token - 1, + ), ), Value( size=0, value=None, - win=False, - cas_token=expected_cas_token - 1, + flags=ResponseFlags( + win=False, + cas_token=expected_cas_token - 1, + ), ), Value( size=len(encoded_value.data), value=None, - cas_token=expected_cas_token, - client_flag=encoded_value.encoding_id, + flags=ResponseFlags( + cas_token=expected_cas_token, + client_flag=encoded_value.encoding_id, + ), ), ] memcache_socket.get_value.side_effect = [b"", b"", encoded_value.data] @@ -863,20 +886,26 @@ def test_get_or_lease_miss_lost_then_win( Value( size=0, value=None, - win=False, - cas_token=expected_cas_token - 1, + flags=ResponseFlags( + win=False, + cas_token=expected_cas_token - 1, + ), ), Value( size=0, value=None, - win=False, - cas_token=expected_cas_token - 1, + flags=ResponseFlags( + win=False, + cas_token=expected_cas_token - 1, + ), ), Value( size=0, value=None, - win=True, - cas_token=expected_cas_token, + flags=ResponseFlags( + win=True, + cas_token=expected_cas_token, + ), ), ] memcache_socket.get_value.side_effect = [b"", b"", b""] @@ -912,8 +941,10 @@ def test_get_or_lease_miss_runs_out_of_retries( memcache_socket.get_response.return_value = Value( size=0, value=None, - win=False, - cas_token=expected_cas_token, + flags=ResponseFlags( + win=False, + cas_token=expected_cas_token, + ), ) memcache_socket.get_value.return_value = b"" @@ -1202,7 +1233,7 @@ def test_delta_cmd(memcache_socket: MemcacheSocket, cache_client: CacheClient) - memcache_socket.sendall.reset_mock() memcache_socket.get_response.reset_mock() - memcache_socket.get_response.return_value = Success() + memcache_socket.get_response.return_value = Success(flags=ResponseFlags()) result = cache_client.delta(key=Key("foo"), delta=1) assert result is True @@ -1210,7 +1241,9 @@ def test_delta_cmd(memcache_socket: MemcacheSocket, cache_client: CacheClient) - memcache_socket.sendall.reset_mock() memcache_socket.get_response.reset_mock() - memcache_socket.get_response.return_value = Value(size=2, value=None) + memcache_socket.get_response.return_value = Value( + size=2, value=None, flags=ResponseFlags() + ) memcache_socket.get_value.return_value = b"10" result = cache_client.delta_and_get(key=Key("foo"), delta=1) @@ -1234,8 +1267,16 @@ def test_delta_cmd(memcache_socket: MemcacheSocket, cache_client: CacheClient) - def test_multi_get(memcache_socket: MemcacheSocket, cache_client: CacheClient) -> None: memcache_socket.get_response.side_effect = [ Miss(), - Value(size=2, value=None, client_flag=MixedSerializer.BINARY), - Value(size=2, value=None, win=True), + Value( + size=2, + value=None, + flags=ResponseFlags(client_flag=MixedSerializer.BINARY), + ), + Value( + size=2, + value=None, + flags=ResponseFlags(win=True), + ), ] memcache_socket.get_value.return_value = b"OK" diff --git a/tests/memcache_socket_test.py b/tests/memcache_socket_test.py index 796b21b..455c17d 100644 --- a/tests/memcache_socket_test.py +++ b/tests/memcache_socket_test.py @@ -67,11 +67,11 @@ def test_get_response( ms = MemcacheSocket(fake_socket) result = ms.get_response() assert isinstance(result, Success) - assert result.cas_token == 1 + assert result.flags.cas_token == 1 result = ms.get_response() assert isinstance(result, Value) - assert result.cas_token == 1 + assert result.flags.cas_token == 1 assert result.size == 2 @@ -84,11 +84,11 @@ def test_get_response_1_6_6( ms = MemcacheSocket(fake_socket, version=ServerVersion.AWS_1_6_6) result = ms.get_response() assert isinstance(result, Success) - assert result.cas_token == 1 + assert result.flags.cas_token == 1 result = ms.get_response() assert isinstance(result, Value) - assert result.cas_token == 1 + assert result.flags.cas_token == 1 assert result.size == 2 @@ -121,7 +121,7 @@ def test_get_value( ms = MemcacheSocket(fake_socket) result = ms.get_response() assert isinstance(result, Value) - assert result.cas_token == 1 + assert result.flags.cas_token == 1 assert result.size == 2 ms.get_value(2) @@ -135,9 +135,9 @@ def test_get_value_large( ms = MemcacheSocket(fake_socket, buffer_size=100) result = ms.get_response() assert isinstance(result, Value) - assert result.cas_token == 1 - assert result.win is True - assert result.opaque == b"xxx" + assert result.flags.cas_token == 1 + assert result.flags.win is True + assert bytes(result.flags.opaque) == b"xxx" assert result.size == 200 value = ms.get_value(result.size) assert len(value) == result.size diff --git a/tests/migrating_cache_client_test.py b/tests/migrating_cache_client_test.py index bcb1749..9e1ceef 100644 --- a/tests/migrating_cache_client_test.py +++ b/tests/migrating_cache_client_test.py @@ -2,7 +2,15 @@ from meta_memcache.interfaces.router import DEFAULT_FAILURE_HANDLING import pytest -from meta_memcache import CacheClient, IntFlag, Key, Value, WriteFailureEvent +from meta_memcache import ( + CacheClient, + IntFlag, + Key, + SetMode, + Value, + WriteFailureEvent, + ResponseFlags, +) from meta_memcache.extras.migrating_cache_client import ( MigratingCacheClient, MigrationMode, @@ -75,13 +83,17 @@ def _set_cache_client_mock_get_return_values(client: Mock, ttl: int = 10) -> Non client.meta_get.return_value = Value( size=3, value="bar", - ttl=ttl, + flags=ResponseFlags( + ttl=ttl, + ), ) client.meta_multiget.return_value = { Key(key="foo", routing_key=None, is_unicode=False): Value( size=3, value="bar", - ttl=ttl, + flags=ResponseFlags( + ttl=ttl, + ), ) } diff --git a/tests/probabilistic_hot_cache_test.py b/tests/probabilistic_hot_cache_test.py index 728bdc5..4e02fcf 100644 --- a/tests/probabilistic_hot_cache_test.py +++ b/tests/probabilistic_hot_cache_test.py @@ -13,7 +13,7 @@ ProbabilisticHotCache, ) from meta_memcache.metrics.prometheus import PrometheusMetricsCollector -from meta_memcache.protocol import Flag, Miss, ReadResponse, TokenFlag +from meta_memcache.protocol import Flag, Miss, ReadResponse, TokenFlag, ResponseFlags @pytest.fixture @@ -29,8 +29,10 @@ def meta_get( return Value( size=1, value=1, - fetched=1, - last_access=1, + flags=ResponseFlags( + fetched=True, + last_access=1, + ), ) elif key.key.endswith("miss"): return Miss() @@ -38,8 +40,10 @@ def meta_get( return Value( size=1, value=1, - fetched=1, - last_access=9999, + flags=ResponseFlags( + fetched=True, + last_access=9999, + ), ) def meta_multiget( @@ -639,8 +643,10 @@ def test_stale_expires( client.meta_get.side_effect = lambda *args, **kwargs: Value( size=1, value=1, - fetched=1, - last_access=9999, + flags=ResponseFlags( + fetched=True, + last_access=9999, + ), ) # The item will no longer be in the hot cache