图片解析应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
4.1 KiB

  1. """Zookeeper Counter
  2. :Maintainer: None
  3. :Status: Unknown
  4. """
  5. from kazoo.exceptions import BadVersionError
  6. from kazoo.retry import ForceRetryError
  7. import struct
  8. class Counter(object):
  9. """Kazoo Counter
  10. A shared counter of either int or float values. Changes to the
  11. counter are done atomically. The general retry policy is used to
  12. retry operations if concurrent changes are detected.
  13. The data is marshaled using `repr(value)` and converted back using
  14. `type(counter.default)(value)` both using an ascii encoding. As
  15. such other data types might be used for the counter value.
  16. If you would like to support clients updating the same znode path using
  17. either kazoo's counter recipe or curator's SharedCount recipe, you will
  18. need to enable the support_curator flag. This flag limits
  19. support to integers only and does not use ascii encoding as described
  20. above.
  21. Counter changes can raise
  22. :class:`~kazoo.exceptions.BadVersionError` if the retry policy
  23. wasn't able to apply a change.
  24. Example usage:
  25. .. code-block:: python
  26. zk = KazooClient()
  27. zk.start()
  28. counter = zk.Counter("/int")
  29. counter += 2
  30. counter -= 1
  31. counter.value == 1
  32. counter.pre_value == 2
  33. counter.post_value == 1
  34. counter = zk.Counter("/float", default=1.0)
  35. counter += 2.0
  36. counter.value == 3.0
  37. counter.pre_value == 1.0
  38. counter.post_value == 3.0
  39. counter = zk.Counter("/curator", support_curator=True)
  40. counter += 2
  41. counter -= 1
  42. counter.value == 1
  43. counter.pre_value == 2
  44. counter.post_value == 1
  45. """
  46. def __init__(self, client, path, default=0, support_curator=False):
  47. """Create a Kazoo Counter
  48. :param client: A :class:`~kazoo.client.KazooClient` instance.
  49. :param path: The counter path to use.
  50. :param default: The default value to use for new counter paths.
  51. :param support_curator: Enable if support for curator's SharedCount
  52. recipe is desired.
  53. """
  54. self.client = client
  55. self.path = path
  56. self.default = default
  57. self.default_type = type(default)
  58. self.support_curator = support_curator
  59. self._ensured_path = False
  60. self.pre_value = None
  61. self.post_value = None
  62. if self.support_curator and not isinstance(self.default, int):
  63. raise TypeError(
  64. "when support_curator is enabled the default "
  65. "type must be an int"
  66. )
  67. def _ensure_node(self):
  68. if not self._ensured_path:
  69. # make sure our node exists
  70. self.client.ensure_path(self.path)
  71. self._ensured_path = True
  72. def _value(self):
  73. self._ensure_node()
  74. old, stat = self.client.get(self.path)
  75. if self.support_curator:
  76. old = struct.unpack(">i", old)[0] if old != b"" else self.default
  77. else:
  78. old = old.decode("ascii") if old != b"" else self.default
  79. version = stat.version
  80. data = self.default_type(old)
  81. return data, version
  82. @property
  83. def value(self):
  84. return self._value()[0]
  85. def _change(self, value):
  86. if not isinstance(value, self.default_type):
  87. raise TypeError("invalid type for value change")
  88. self.client.retry(self._inner_change, value)
  89. return self
  90. def _inner_change(self, value):
  91. self.pre_value, version = self._value()
  92. post_value = self.pre_value + value
  93. if self.support_curator:
  94. data = struct.pack(">i", post_value)
  95. else:
  96. data = repr(post_value).encode("ascii")
  97. try:
  98. self.client.set(self.path, data, version=version)
  99. except BadVersionError: # pragma: nocover
  100. self.post_value = None
  101. raise ForceRetryError()
  102. self.post_value = post_value
  103. def __add__(self, value):
  104. """Add value to counter."""
  105. return self._change(value)
  106. def __sub__(self, value):
  107. """Subtract value from counter."""
  108. return self._change(-value)