Running an action periodically

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Running an action periodically

Tristan Seligmann-3
I just wrote this helper:

> import Control.Concurrent
> import Control.Concurrent.STM
> import Control.Exception.Safe
> pollT :: Int -> IO a -> IO (STM (Maybe a), Async b)
> pollT delay act = do
>   tv <- atomically (newTVar Nothing)
>   as <-
>     async . forever $ do
>       r <- tryAny act
>       case r of
>         Left _ -> pure ()
>         Right r' -> atomically (writeTVar tv (Just r'))
>       threadDelay delay
>   pure (readTVar tv, as)

I was sort of surprised not to find something like this in an existing library.
  1. Did I miss an existing implementation?
  2. Any problems with this one?
  3. Any suggestions for a better name?
  4. Any thoughts on the general idea? That is, "run an action periodically, updating a TVar with the result".
There's a couple of obvious variations that can be built on top of this, like retrying if the TVar is Nothing when reading from it, or writing Nothing to the TVar when the action fails rather than keeping the old value. Maybe passing in the old value to the action?

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Running an action periodically

Moritz Schulte
Hi Tristan,

how about the async-refresh package
(https://hackage.haskell.org/package/async-refresh)?

Looks close to what you have written — from the README:

About

This is Haskell library implementing the logic for refreshing of
expiring data according to user-provided actions.

Usage

* Create a new configuration using newAsyncRefreshConf, providing the
  action to be used for data refreshing.

* Adjust the configuration using the asyncRefreshConfSet* functions,
  in particular using asyncRefreshConfSetCallback.

* Use newAsyncRefresh to initiate a new thread managing the
  asynchronous refreshing.

[...]

It is currently used by the package async-refresh-tokens
(https://hackage.haskell.org/package/async-refresh-tokens), which
specializes the async-refresh package to the refreshing of expiring
authentication tokens.

If you have questions or suggestions, feel free to open issues and/or
contact me directly.

Best,
Moritz
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Running an action periodically

Erèbe
In reply to this post by Tristan Seligmann-3
You have the auto-update package that provide that kind of functionnality
https://hackage.haskell.org/package/auto-update-0.1.4/docs/Control-AutoUpdate.html

2017-11-24 21:53 GMT+01:00 Tristan Seligmann <[hidden email]>:
I just wrote this helper:

> import Control.Concurrent
> import Control.Concurrent.STM
> import Control.Exception.Safe
> pollT :: Int -> IO a -> IO (STM (Maybe a), Async b)
> pollT delay act = do
>   tv <- atomically (newTVar Nothing)
>   as <-
>     async . forever $ do
>       r <- tryAny act
>       case r of
>         Left _ -> pure ()
>         Right r' -> atomically (writeTVar tv (Just r'))
>       threadDelay delay
>   pure (readTVar tv, as)

I was sort of surprised not to find something like this in an existing library.
  1. Did I miss an existing implementation?
  2. Any problems with this one?
  3. Any suggestions for a better name?
  4. Any thoughts on the general idea? That is, "run an action periodically, updating a TVar with the result".
There's a couple of obvious variations that can be built on top of this, like retrying if the TVar is Nothing when reading from it, or writing Nothing to the TVar when the action fails rather than keeping the old value. Maybe passing in the old value to the action?

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Running an action periodically

Moritz Schulte
Hi Erèbe,

Thanks for the suggestion.

I've only had a very quick look at the auto-update package, so please
correct me if I'm wrong. But I think there is an important difference
between auto-update and the suggested pollT (or the async-refresh
package):

auto-update does not completely decouple the execution of the IO
action from the calling thread. I'm referring to this comment:

   mkAutoUpdate :: UpdateSettings a -> IO (IO a)

   Generate an action which will either read from an automatically
   updated value, **or run the update action in the current thread**.

pollT and async-refresh are implemented such that the caller really
only retrieves the result of a previously executed IO action.  Thus
even if the IO action might block, the caller does basically not block
(more than required for reading a TVar).

For the use case it was created for (refreshing of authentication
tokens in a micro service) it might be very important that the
required tokens are *always* guaranteed to be available already at the
time a request needs to be made.

But maybe I'm overlooking something and auto-update does indeed also
provide this functionality.

Best,
Moritz
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Running an action periodically

Erèbe
Hello Moritz,

Indeed, the auto-update do not do any refresh if the returned action is not called.
So in your case your token will expire, which is not the desired behavior.

which allow you to run periodically an action on an associated resource while still providing the possibility to read and append from this resource.

In your case it should fit by using the clean action as the refresh job, and using reaperRead to get your token.
While this being said, using reaper may be a bit far fetched so using async-refresh or pollT may be a better call :)


2017-11-25 23:53 GMT+01:00 Moritz Schulte <[hidden email]>:
Hi Erèbe,

Thanks for the suggestion.

I've only had a very quick look at the auto-update package, so please
correct me if I'm wrong. But I think there is an important difference
between auto-update and the suggested pollT (or the async-refresh
package):

auto-update does not completely decouple the execution of the IO
action from the calling thread. I'm referring to this comment:

   mkAutoUpdate :: UpdateSettings a -> IO (IO a)

   Generate an action which will either read from an automatically
   updated value, **or run the update action in the current thread**.

pollT and async-refresh are implemented such that the caller really
only retrieves the result of a previously executed IO action.  Thus
even if the IO action might block, the caller does basically not block
(more than required for reading a TVar).

For the use case it was created for (refreshing of authentication
tokens in a micro service) it might be very important that the
required tokens are *always* guaranteed to be available already at the
time a request needs to be made.

But maybe I'm overlooking something and auto-update does indeed also
provide this functionality.

Best,
Moritz
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.