Skip to content

Commit

Permalink
Merge pull request #19 from evo-company/add-sync-method-to-client
Browse files Browse the repository at this point in the history
allow to push defaults during flags preload
  • Loading branch information
kindermax authored Jul 18, 2023
2 parents 68d5a85 + a4fd060 commit 459d216
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 10 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ examples/local_config.py
.pytest_cache
.lets
.pdm.toml
__pypackages__
__pypackages__
.DS_Store
15 changes: 15 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@ Server consists of two services:

- grpclib_ + hiku_

Installation
~~~~~~~~~~~~

To install client library for synchronous app:

.. code-block:: shell
$ pip install featureflags-client grpcio
To install client library for asynchronous app:

.. code-block:: shell
$ pip install featureflags-client grpclib
Development
~~~~~~~~~~~

Expand Down
18 changes: 18 additions & 0 deletions client/featureflags/client/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ def flush(self, delta=timedelta(minutes=1)):
))
return stats

@staticmethod
def from_defaults(defaults):
interval_pb = Timestamp()
interval_pb.FromDatetime(datetime.utcnow())
return [
FlagUsage(name=name, interval=interval_pb, positive_count=0, negative_count=0)
for name in defaults
]


class Tracer:
"""
Expand Down Expand Up @@ -216,3 +225,12 @@ def flags(
finally:
if tracer is not None:
self._manager.add_trace(tracer)

def preload(self, timeout=None):
"""Preload flags from server.
This method syncs all flags with server"""
self._manager.preload(timeout=timeout, defaults=self._defaults)

async def preload_async(self, timeout=None):
"""Async version of `preload` method"""
await self._manager.preload(timeout=timeout, defaults=self._defaults)
23 changes: 19 additions & 4 deletions client/featureflags/client/managers/asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,20 @@ def __init__(self, project, variables, channel, *, loop=None):
stacklevel=2,
)

async def preload(self, *, timeout=None):
await self._exchange(timeout)
async def preload(self, *, timeout=None, defaults=None):
"""
Preload flags from the server.
:param timeout: timeout in seconds (for grpclib)
:param defaults: dict with default values for feature flags.
If passed, all feature flags will be synced with server,
otherwise flags will be synced only when they are accessed
for the first time.
"""
stats = None
if defaults is not None:
stats = self._stats.from_defaults(defaults)

await self._exchange(timeout, stats)

def start(self):
if self._exchange_task is not None:
Expand Down Expand Up @@ -105,8 +117,11 @@ async def _exchange_coro(self):
await asyncio.sleep(interval)
continue

async def _exchange(self, timeout):
request = self._state.get_request(self._stats.flush())
async def _exchange(self, timeout, flags_usage=None):
if flags_usage is None:
flags_usage = self._stats.flush()

request = self._state.get_request(flags_usage)
log.debug('Exchange request, project: %r, version: %r, stats: %r',
request.project, request.version, request.flags_usage)
reply = await self._stub.Exchange(request, timeout=timeout)
Expand Down
25 changes: 20 additions & 5 deletions client/featureflags/client/managers/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,26 @@ def __init__(self, project, variables, channel):
self._int_gen.send(None)
self._next_exchange = datetime.utcnow()

def preload(self, timeout=None):
self._exchange(timeout)

def _exchange(self, timeout):
request = self._state.get_request(self._stats.flush())
def preload(self, timeout=None, defaults=None):
"""
Preload flags from the server.
:param timeout: timeout in seconds (for grpcio)
:param defaults: dict with default values for feature flags.
If passed, all feature flags will be synced with server,
otherwise flags will be synced only when they are accessed
for the first time.
"""
stats = None
if defaults is not None:
stats = self._stats.from_defaults(defaults)

self._exchange(timeout, stats)

def _exchange(self, timeout, flags_usage=None):
if flags_usage is None:
flags_usage = self._stats.flush()

request = self._state.get_request(flags_usage)
log.debug('Exchange request, project: %r, version: %r, stats: %r',
request.project, request.version, request.flags_usage)
reply = self._stub.Exchange(request, timeout=timeout)
Expand Down

0 comments on commit 459d216

Please sign in to comment.