improvement: Add a jobs and functions package #4

Merged
k-simons merged 41 commits from ksimons/jobs into develop 2025-12-16 17:45:13 +00:00
k-simons commented 2025-12-16 03:23:49 +00:00 (Migrated from github.com)
  • Adds a new function package with generic functional primitives (Function, Consumer, Supplier, Runnable, NamedRunnable) that provide type-safe wrappers for common function patterns with context and error handling
  • Adds a new jobs package that provides a JobRunner for executing periodic background tasks with health check integration, configurable intervals, and error handling
  • Updates the runnable package to use function.NamedRunnable consistently across NewSequential, NewParallel, and NewForever functions, ensuring compatibility with the Wrapper type which requires named runnables
- Adds a new function package with generic functional primitives (Function, Consumer, Supplier, Runnable, NamedRunnable) that provide type-safe wrappers for common function patterns with context and error handling - Adds a new jobs package that provides a JobRunner for executing periodic background tasks with health check integration, configurable intervals, and error handling - Updates the runnable package to use function.NamedRunnable consistently across NewSequential, NewParallel, and NewForever functions, ensuring compatibility with the Wrapper type which requires named runnables
changelog-app[bot] commented 2025-12-16 03:23:53 +00:00 (Migrated from github.com)

Generate changelog in changelog/@unreleased

Type (Select exactly one)

  • Feature (Adding new functionality)
  • Improvement (Improving existing functionality)
  • Fix (Fixing an issue with existing functionality)
  • Break (Creating a new major version by breaking public APIs)
  • Deprecation (Removing functionality in a non-breaking way)
  • Migration (Automatically moving data/functionality to a new system)

Description Ksimons/jobs

Check the box to generate changelog(s)

  • Generate changelog entry
### Generate changelog in <changelog-dir>`changelog/@unreleased`</changelog-dir> **Type** _(Select exactly one)_ <changelog-type> - [ ] **Feature** (Adding new functionality) - [ ] **Improvement** (Improving existing functionality) - [ ] **Fix** (Fixing an issue with existing functionality) - [ ] **Break** (Creating a _new major version_ by breaking public APIs) - [ ] **Deprecation** (Removing functionality in a non-breaking way) - [ ] **Migration** (Automatically moving data/functionality to a new system) </changelog-type> **Description** <changelog-description> Ksimons/jobs </changelog-description><changelog-links></changelog-links> **Check the box to generate changelog(s)** - [ ] Generate changelog entry
changelog-app[bot] commented 2025-12-16 03:41:43 +00:00 (Migrated from github.com)

✅ Successfully generated changelog entry!

Entry generated via PR title

To modify this entry, edit PR title using proper format.


📋Changelog Preview

💡 Improvements

  • Add a jobs and functions package (#4)
### ✅ **Successfully generated changelog entry!** #### Entry generated via PR title To modify this entry, edit PR title using proper format. --- ### 📋Changelog Preview ### 💡 Improvements * Add a jobs and functions package ([#4](https://github.com/palantir/witchcraft-go-tasks/pull/4)) <!-- changelog-success-comment -->
nmiyake (Migrated from github.com) requested changes 2025-12-16 06:28:12 +00:00
nmiyake (Migrated from github.com) left a comment

Thanks! Core functionality looks good.

Most of my feedback is on documentation, although also had a few suggestions/questions about generic type naming and parameter order.

Made suggestions for consideration, but I'm flexible on the specifics.

Thanks! Core functionality looks good. Most of my feedback is on documentation, although also had a few suggestions/questions about generic type naming and parameter order. Made suggestions for consideration, but I'm flexible on the specifics.
@ -0,0 +1,50 @@
// Copyright (c) 2025 Palantir Technologies. All rights reserved.
nmiyake (Migrated from github.com) commented 2025-12-16 05:18:47 +00:00
// ConsumerFunc is a named type for a function that implements the Consumer interface.

A bit of a nit, but Go 1.9 introduced "type aliases", which use the = operator (for example, type StringAlias = string), so would avoid using "type alias" as the term here since it's not technically correct.

```suggestion // ConsumerFunc is a named type for a function that implements the Consumer interface. ``` A bit of a nit, but Go 1.9 introduced "type aliases", which use the `=` operator (for example, `type StringAlias = string`), so would avoid using "type alias" as the term here since it's not technically correct.
nmiyake (Migrated from github.com) commented 2025-12-16 05:23:56 +00:00
// NewConsumerFromFunction returns a Consumer[T] created from the provided Function[T, R]. The returned Consumer calls the Apply function of the provided Function with the T argument, ignores the R value returned by the function, and returns the error returned by the function.
```suggestion // NewConsumerFromFunction returns a Consumer[T] created from the provided Function[T, R]. The returned Consumer calls the Apply function of the provided Function with the T argument, ignores the R value returned by the function, and returns the error returned by the function. ```
nmiyake (Migrated from github.com) commented 2025-12-16 05:28:01 +00:00
// NewConsumerFromFunc returns a Consumer[T] by using type conversion to convert the provided function to a ConsumerFunc.

nit, but technically referred to as type conversion in Go rather than type casting.

```suggestion // NewConsumerFromFunc returns a Consumer[T] by using type conversion to convert the provided function to a ConsumerFunc. ``` nit, but technically referred to as type conversion in Go rather than type casting.
@ -0,0 +1,42 @@
// Copyright (c) 2025 Palantir Technologies. All rights reserved.
nmiyake (Migrated from github.com) commented 2025-12-16 05:26:13 +00:00
// FunctionFunc is a named type for a function that satisfies the Function interface
```suggestion // FunctionFunc is a named type for a function that satisfies the Function interface ```
nmiyake (Migrated from github.com) commented 2025-12-16 05:28:29 +00:00
// NewFunctionFromFunc returns a Function[T, R] by using type conversion to convert the provided function to a FunctionFunc.
```suggestion // NewFunctionFromFunc returns a Function[T, R] by using type conversion to convert the provided function to a FunctionFunc. ```
@ -0,0 +1,68 @@
// Copyright (c) 2025 Palantir Technologies. All rights reserved.
nmiyake (Migrated from github.com) commented 2025-12-16 05:29:21 +00:00
// RunnableFunc is a named type for a function that implements the Runnable interface.
```suggestion // RunnableFunc is a named type for a function that implements the Runnable interface. ```
nmiyake (Migrated from github.com) commented 2025-12-16 05:30:10 +00:00
// NewRunnableFromFunc creates an interface of Runnable by using type conversion to convertthe provided function to a RunnableFunc.
```suggestion // NewRunnableFromFunc creates an interface of Runnable by using type conversion to convertthe provided function to a RunnableFunc. ```
nmiyake (Migrated from github.com) commented 2025-12-16 05:32:08 +00:00
// NewRunnableFromConsumer returns a Runnable based on the provided Consumer[T] and argument. The returned Runnable's Run function calls the Accept function of the provided consumer with the provided argument and returns the error returned by the function.
```suggestion // NewRunnableFromConsumer returns a Runnable based on the provided Consumer[T] and argument. The returned Runnable's Run function calls the Accept function of the provided consumer with the provided argument and returns the error returned by the function. ```
nmiyake (Migrated from github.com) commented 2025-12-16 05:39:40 +00:00
// NewRunnableFromFunction returns a Runnable created from the provided Function[T, R] and argument. The returned Runnable's Run function calls the Apply function of the provided Function with the provided argument, ignores the R value returned by the function, and returns the error returned by the function.
```suggestion // NewRunnableFromFunction returns a Runnable created from the provided Function[T, R] and argument. The returned Runnable's Run function calls the Apply function of the provided Function with the provided argument, ignores the R value returned by the function, and returns the error returned by the function. ```
nmiyake (Migrated from github.com) commented 2025-12-16 05:41:57 +00:00
// NewRunnableFromSupplier returns a Runnable created from the provided Supplier[R]. The returned Runnable's Run function calls the Get function of the provided Supplier, ignores the R value returned by the function, and returns the error returned by the function.
```suggestion // NewRunnableFromSupplier returns a Runnable created from the provided Supplier[R]. The returned Runnable's Run function calls the Get function of the provided Supplier, ignores the R value returned by the function, and returns the error returned by the function. ```
nmiyake (Migrated from github.com) commented 2025-12-16 05:45:14 +00:00

Fine to keep this as is if it's already well-established, but for both this function and the next one, it feels more natural to me to have the function/consumer parameter as the first parameter and the arg parameter as the second parameter.

Fine to keep this as is if it's already well-established, but for both this function and the next one, it feels more natural to me to have the `function`/`consumer` parameter as the first parameter and the `arg` parameter as the second parameter.
nmiyake (Migrated from github.com) commented 2025-12-16 05:47:15 +00:00
func NewRunnableFromSupplier[R any](supplier Supplier[R]) Runnable {

Recommending change above -- c seems strange as the name given that Supplier doesn't start with c, and for all other arguments in this file we spell them out (consumer, function, etc.) rather than abbreviate, so seems strange to just do that here.

R also feels more consistent given that for all the other generic functions in this file T is the input type and R is the return type.

```suggestion func NewRunnableFromSupplier[R any](supplier Supplier[R]) Runnable { ``` Recommending change above -- `c` seems strange as the name given that `Supplier` doesn't start with `c`, and for all other arguments in this file we spell them out (`consumer`, `function`, etc.) rather than abbreviate, so seems strange to just do that here. `R` also feels more consistent given that for all the other generic functions in this file `T` is the input type and `R` is the return type.
nmiyake (Migrated from github.com) commented 2025-12-16 06:15:40 +00:00
// NamedRunnable is a Runnable that has a name.
```suggestion // NamedRunnable is a Runnable that has a name. ```
@ -0,0 +31,4 @@
type NamedRunnable interface {
Runnable
// Name returns the name for the runnable. A Runnable's name is mostly informational, and generally used to provide a human-readable name for a runnable that can be logged or used for debugging.
Name() string
nmiyake (Migrated from github.com) commented 2025-12-16 06:18:35 +00:00
    // Name returns the name for the runnable. A Runnable's name is mostly informational, and generally used to provide a human-readable name for a runnable that can be logged or used for debugging.
	Name() string

Added the documentation based on what I believe to be true from seeing uses and thinking about it -- I think it's helpful to clarify. The above also implies that names don't necessarily have to be unique or namespaced (if that is a requirement/consumers are allowed to assume all jobs have unique names, then I think that's something that should be documented).

```suggestion // Name returns the name for the runnable. A Runnable's name is mostly informational, and generally used to provide a human-readable name for a runnable that can be logged or used for debugging. Name() string ``` Added the documentation based on what I believe to be true from seeing uses and thinking about it -- I think it's helpful to clarify. The above also implies that names don't necessarily have to be unique or namespaced (if that is a requirement/consumers are allowed to assume all jobs have unique names, then I think that's something that should be documented).
@ -0,0 +1,56 @@
// Copyright (c) 2025 Palantir Technologies. All rights reserved.
nmiyake (Migrated from github.com) commented 2025-12-16 05:48:55 +00:00
// Supplier is a generic interface for applying a function that returns type R

Per question/suggestion above, would it make more sense to have this type be R to standardize on R being the return type generic name? Could go either way, but that was my thought.

```suggestion // Supplier is a generic interface for applying a function that returns type R ``` Per question/suggestion above, would it make more sense to have this type be `R` to standardize on `R` being the return type generic name? Could go either way, but that was my thought.
nmiyake (Migrated from github.com) commented 2025-12-16 05:49:47 +00:00
// SupplierFunc is a named type for a function that implements the Supplier interface.
```suggestion // SupplierFunc is a named type for a function that implements the Supplier interface. ```
nmiyake (Migrated from github.com) commented 2025-12-16 05:50:30 +00:00
// NewSupplierFromFunc returns a Supplier[T] by by using type conversion to convert the provided function to a  SupplierFunc.
```suggestion // NewSupplierFromFunc returns a Supplier[T] by by using type conversion to convert the provided function to a SupplierFunc. ```
nmiyake (Migrated from github.com) commented 2025-12-16 05:52:08 +00:00
// NewSupplierFromFunction returns a Supplier[R] created from the provided Function[T, R] and argument. The returned Supplier's Get function calls the Apply function of the provided Function with the provided argument, ignores the R value returned by the function, and returns the error returned by the function.
```suggestion // NewSupplierFromFunction returns a Supplier[R] created from the provided Function[T, R] and argument. The returned Supplier's Get function calls the Apply function of the provided Function with the provided argument, ignores the R value returned by the function, and returns the error returned by the function. ```
nmiyake (Migrated from github.com) commented 2025-12-16 05:53:02 +00:00
// NewSupplierFromValue returns a Supplier[R] that implements the Get function by returning the provided value of type R.
```suggestion // NewSupplierFromValue returns a Supplier[R] that implements the Get function by returning the provided value of type R. ```
nmiyake (Migrated from github.com) commented 2025-12-16 05:59:29 +00:00
func NewSupplierFromValue[R any](value R) Supplier[R] {

(If we go with suggestion of renaming to R -- otherwise can keep as-is)

```suggestion func NewSupplierFromValue[R any](value R) Supplier[R] { ``` (If we go with suggestion of renaming to `R` -- otherwise can keep as-is)
@ -0,0 +1,41 @@
// Copyright (c) 2025 Palantir Technologies. All rights reserved.
nmiyake (Migrated from github.com) commented 2025-12-16 06:02:02 +00:00
// JobRunner is an interface for that runs a collection of provided Jobs.
```suggestion // JobRunner is an interface for that runs a collection of provided Jobs. ```
@ -0,0 +28,4 @@
// rather than waiting for the first interval to elapse.
ShouldStartImmediately(ctx context.Context) bool
// GetInterval returns the duration between job executions.
GetInterval(ctx context.Context) time.Duration
nmiyake (Migrated from github.com) commented 2025-12-16 06:00:50 +00:00

Is there any guidance around expected behavior / general contracts for these functions?

Is there any guidance around expected behavior / general contracts for these functions?
@ -0,0 +37,4 @@
type JobRunner interface {
// StartJobs begins executing all provided jobs asynchronously.
// Each job runs in its own goroutine and will continue until the context is cancelled.
StartJobs(ctx context.Context, jobs []Job)
nmiyake (Migrated from github.com) commented 2025-12-16 06:02:27 +00:00

Are there any expected behaviors/contracts for this, or is it totally up to the implementor?

Are there any expected behaviors/contracts for this, or is it totally up to the implementor?
@ -0,0 +1,80 @@
// Copyright (c) 2025 Palantir Technologies. All rights reserved.
nmiyake (Migrated from github.com) commented 2025-12-16 06:04:11 +00:00
// NewDefaultJob returns a default Job implementation with the provided name and runnable configured using the provided options.
```suggestion // NewDefaultJob returns a default Job implementation with the provided name and runnable configured using the provided options. ```
@ -0,0 +1,58 @@
// Copyright (c) 2025 Palantir Technologies. All rights reserved.
nmiyake (Migrated from github.com) commented 2025-12-16 06:05:28 +00:00
// JobOption is an option that can be used to configure Jobs created using the NewDefaultJob function.
```suggestion // JobOption is an option that can be used to configure Jobs created using the NewDefaultJob function. ```
@ -0,0 +1,95 @@
// Copyright (c) 2025 Palantir Technologies. All rights reserved.
nmiyake (Migrated from github.com) commented 2025-12-16 06:10:50 +00:00

Is this something that can be fixed now or is still carried over?

Is this something that can be fixed now or is still carried over?
nmiyake (Migrated from github.com) commented 2025-12-16 06:11:45 +00:00

Might be helpful to describe parameter and the general behavior of the default runner (particularly how the provided KeyedErrorHealthCheckSource is used?)

Might be helpful to describe parameter and the general behavior of the default runner (particularly how the provided `KeyedErrorHealthCheckSource` is used?)
nmiyake (Migrated from github.com) commented 2025-12-16 06:24:11 +00:00

The parameter is named k8sKeyedErrorHealthCheckSource -- since the definition here is fairly generic and doesn't otherwise reference/assume k8s, should it just be healthCheckSource or keyedErrorHealthCheckSource?

The parameter is named `k8sKeyedErrorHealthCheckSource` -- since the definition here is fairly generic and doesn't otherwise reference/assume k8s, should it just be `healthCheckSource` or `keyedErrorHealthCheckSource`?
nmiyake (Migrated from github.com) commented 2025-12-16 06:25:01 +00:00
	keyedErrorHealthCheckSource window.KeyedErrorHealthCheckSource

Per other comment below, might be better to omit k8s from name for the generic type/struct unless there's a reason that it would always have to be k8s-related

```suggestion keyedErrorHealthCheckSource window.KeyedErrorHealthCheckSource ``` Per other comment below, might be better to omit `k8s` from name for the generic type/struct unless there's a reason that it would always have to be k8s-related
@ -0,0 +56,4 @@
}
// StartJobs starts all of the provided jobs. Creates a new goroutine for each job.
func (d defaultJobRunner) StartJobs(ctx context.Context, jobs []Job) {
nmiyake (Migrated from github.com) commented 2025-12-16 06:10:37 +00:00
// StartJobs starts all of the provided jobs. Creates a new goroutine for each job.
func (d defaultJobRunner) StartJobs(ctx context.Context, jobs []Job) {
```suggestion // StartJobs starts all of the provided jobs. Creates a new goroutine for each job. func (d defaultJobRunner) StartJobs(ctx context.Context, jobs []Job) { ```
k-simons (Migrated from github.com) reviewed 2025-12-16 15:27:28 +00:00
@ -0,0 +28,4 @@
// rather than waiting for the first interval to elapse.
ShouldStartImmediately(ctx context.Context) bool
// GetInterval returns the duration between job executions.
GetInterval(ctx context.Context) time.Duration
k-simons (Migrated from github.com) commented 2025-12-16 15:27:28 +00:00

Done

Done
k-simons (Migrated from github.com) reviewed 2025-12-16 15:28:38 +00:00
@ -0,0 +1,95 @@
// Copyright (c) 2025 Palantir Technologies. All rights reserved.
k-simons (Migrated from github.com) commented 2025-12-16 15:28:37 +00:00

Done

Done
nmiyake (Migrated from github.com) approved these changes 2025-12-16 17:43:24 +00:00
Sign in to join this conversation.
No description provided.