{"id":15069305,"url":"https://github.com/dvgica/periodic","last_synced_at":"2025-04-10T16:52:45.057Z","repository":{"id":199402979,"uuid":"702740070","full_name":"dvgica/periodic","owner":"dvgica","description":"A low-dependency Scala library providing a cached variable that self-updates periodically, and a periodic function runner","archived":false,"fork":false,"pushed_at":"2024-04-08T13:09:20.000Z","size":155,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-04-08T14:29:16.924Z","etag":null,"topics":["cache","jdk","pekko","pekko-streams","periodic","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dvgica.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-10-09T23:19:25.000Z","updated_at":"2024-04-08T14:29:24.626Z","dependencies_parsed_at":"2023-10-30T04:30:44.375Z","dependency_job_id":"c2a0ca7d-722d-4886-861b-5179db0e51d2","html_url":"https://github.com/dvgica/periodic","commit_stats":null,"previous_names":["dvgica/periodic"],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dvgica%2Fperiodic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dvgica%2Fperiodic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dvgica%2Fperiodic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dvgica%2Fperiodic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dvgica","download_url":"https://codeload.github.com/dvgica/periodic/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248256420,"owners_count":21073527,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","jdk","pekko","pekko-streams","periodic","scala"],"created_at":"2024-09-25T01:41:41.614Z","updated_at":"2025-04-10T16:52:45.036Z","avatar_url":"https://github.com/dvgica.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Periodic\n[![Maven](https://img.shields.io/maven-central/v/ca.dvgi/periodic-core_2.13?color=blue)](https://search.maven.org/search?q=g:ca.dvgi%20periodic) [![CI](https://img.shields.io/github/actions/workflow/status/dvgica/periodic/ci.yml?branch=main)](https://github.com/dvgica/periodic/actions)\n\nPeriodic is a low-dependency Scala library providing:\n\n- an in-memory cached variable (`AutoUpdatingVar`) that self-updates on a periodic basis\n- a periodic runner for a side-effecting function (`FnRunner`)\n\nIt attempts to provide an effect- and runtime-agnostic API which can abstract various implementations as needed.\n\n- [Motivation](#motivation)\n- [Installation](#installation)\n- [Usage](#usage)\n- [Contributing](#contributing)\n\n## Motivation\n\nIt is fairly common to need to do something periodically, while a process is running.\n\n### `AutoUpdatingVar`\n`AutoUpdatingVar` is useful for caching semi-static data in memory and having that data be automatically and periodically updated. The source of the data is typically another process that can be queried to get a new data value. If the cached data becomes stale at a predictable interval, the cached data can be updated before this occurs. If the cached data becomes stale at unpredictable times, the stale data must still be usable. Concrete use cases include:\n\n- caching a time-limited key or token, and replacing it with a new one before it expires (e.g. an OAuth access token)\n- caching data that changes irregularly and occasionally, such as a list of a country's airports and their codes\n\nFor data that changes irregularly but must be up-to-date, you likely want to be subscribing to some kind of change event instead.\n\n### `FnRunner`\n`FnRunner` is useful when you want to do something periodically, but don't need to make any data available. Concrete use cases include:\n\n- deleting old records in a database\n- triggering calls to an external service\n\n## Installation\n\nPeriodic is available on Maven Central for Scala 2.12, 2.13, and 3. Java 11+ is required.\n\nFor the **default JDK-based implementation**, add the following dependency:\n\n```\n\"ca.dvgi\" %% \"periodic-core\" % \"\u003clatest\u003e\"\n```\n\nFor the **Pekko Streams-based implementation**, use this dependency:\n\n```\n\"ca.dvgi\" %% \"periodic-pekko-stream\" % \"\u003clatest\u003e\"\n```\n\n### Dependencies\n- `periodic-core` depends only on `slf4j-api`\n- `periodic-pekko-stream` depends on `pekko-stream` and `periodic-core`\n\n## Usage\n\n### Periodic\n\nAll library functionality is based on implementations of `Periodic`. Therefore all classes require an instance of `Periodic` in their constructor. \n\n#### JDK Implementation\n\n`JdkPeriodic` is the default implementation provided in `periodic-core` which is suitable for many use cases. Usages of the `jdk` and `jdkFuture` methods on the `AutoUpdatingVar` and `FnRunner` companion objects create a new, non-shared `JdkPeriodic` (and thus a new thread) for each invocation. This will work well as long as the number of created threads is not problematic for your application.\n\nUsers with many `AutoUpdatingVar`s or `FnRunner`s may wish to share a `JdkPeriodic` between them to decrease the total number of threads used. In this case, the shared `JdkPeriodic` may need to be tuned based on workload. Specifically, users may need to provide a `ScheduledExecutorService` to the shared `JdkPeriodic` with an increased thread count (the default number of threads used by a `JdkPeriodic` is one). Threads in the `ScheduledExecutorService` will be blocked.\n\nThe JDK implementation works out of the box with sync (`Identity`) or async (`scala.concurrent.Future`) functions. If usage with another effect is desired, provide a typeclass implementation of `ca.dvgi.periodic.jdk.Eval`.\n\n#### Pekko Streams Implementation\n\nThe Pekko Streams implementation is completely non-blocking and does not need additional resources besides an `ActorSystem`. A single `PekkoStreamsPeriodic` can be shared by many `AutoUpdatingVar`s and `FnRunner`s without requiring tuning. It is recommended if you are already using Pekko or don't mind the extra dependency. As usual with Pekko-based code, user-provided functions should not block.\n\nThe Pekko Streams implementation only works with `scala.concurrent.Future`.\n\n### `AutoUpdatingVar`\n\n#### Default JDK-based Implementation\n\n``` scala\nimport ca.dvgi.periodic._\nimport scala.concurrent.duration._\nimport scala.concurrent.Await\nimport java.time.Instant\n\ndef updateData(): String = Instant.now.toString\n\nval data = AutoUpdatingVar.jdk( // or AutoUpdatingVar.jdkFuture if updateData returns a Future\n  updateData(),\n  // can also be dynamic based on the last data\n  UpdateInterval.Static(1.second), \n  // can also be finite with configurable behavior for attempt exhaustion\n  AttemptStrategy.Infinite(5.seconds), \n)\n\n// `ready` returns a `Future[Unit]` which completes when the data initialization is complete\n// see also the `blockUntilReadyTimeout` parameter\nAwait.result(data.ready, 5.seconds)\n\n// the data is cached in memory and can be accessed using `latest`\nprintln(s\"Cached data is ${data.latest}\")\nThread.sleep(10)\n// data has not been updated yet, same result\nprintln(s\"Cached data is still ${data.latest}\")\n\nThread.sleep(1100)\n\n// the `AutoUpdatingVar` fetched new data while this thread was sleeping\nprintln(s\"New cached data is ${data.latest}\")\n```\n\nThis results in the following output:\n```\nCached data is 2023-10-19T02:35:22.467418Z\nCached data is still 2023-10-19T02:35:22.467418Z\nNew cached data is 2023-10-19T02:35:23.474155Z\n```\n\nFor handling errors during update, and other options, see the Scaladocs.\n\n#### Pekko Streams Implementation\n\n``` scala\nimport org.apache.pekko.actor.ActorSystem\nimport ca.dvgi.periodic.pekko.stream.PekkoStreamsPeriodic\nimport ca.dvgi.periodic._\nimport scala.concurrent.duration._\nimport scala.concurrent.Future\nimport java.time.Instant\n\ndef updateData(): Future[String] = Future.successful(Instant.now.toString)\n\n// generally you should have an ActorSystem in your process already\nimplicit val actorSystem = ActorSystem() \n\nval data = AutoUpdatingVar(\n  PekkoStreamsPeriodic() // can also be shared by many AutoUpdatingVars or FnRunners\n)(\n  updateData(),\n  UpdateInterval.Static(1.second),\n  AttemptStrategy.Infinite(5.seconds)\n)\n\n```\n\n### `FnRunner`\n\n``` scala\nimport ca.dvgi.periodic._\nimport scala.concurrent.duration._\nimport java.time.Instant\n\ndef doSomething(): FiniteDuration = {\n  println(s\"the time is: ${Instant.now.toString}\")\n  10.seconds\n}\n\n// alternately use FnRunner.jdkFuture or FnRunner.apply(somePeriodic)\nval runner = FnRunner.jdk(doSomething, AttemptStrategy.Infinite(1.second), \"time printer\")\n```\n## Contributing \n\nContributions in the form of Issues and PRs are welcome.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdvgica%2Fperiodic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdvgica%2Fperiodic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdvgica%2Fperiodic/lists"}