You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// Package rill is a Go toolkit designed for efficient and straightforward streaming, parallel processing, and pipeline construction.
2
-
// It abstracts away the complexities of concurrency management, enabling developers to focus on core logic.
3
-
// With features like lightweight integration, batch processing, error handling, and support for functional programming paradigms,
4
-
// rill enhances productivity in building concurrent applications. It offers type-safe operations, and minimizes memory usage even for large data sets.
1
+
// Package rill is a collection of easy-to-use functions for concurrency, streaming, batching and pipeline construction.
2
+
// It abstracts away the complexities of concurrency, removes boilerplate, and provides a structured way to handle errors.
3
+
// Rill is modular and can be easily integrated into existing projects: it requires no setup and allows using only the necessary functions.
4
+
// At the same time, rill's functions can be composed into complex, concurrent, and reusable pipelines when needed.
5
+
//
6
+
// # Streams and Try Containers
7
+
//
8
+
// In this package, a stream refers to a channel of [Try] containers. A Try container is a simple struct that holds a value and an error.
9
+
// When an "empty stream" is referred to, it means a channel of Try containers that has been closed and was never written to.
10
+
//
11
+
// Most functions in this package are concurrent, and the level of concurrency can be controlled by the argument n.
12
+
// Some functions share common behaviors and characteristics, which are described below.
13
+
//
14
+
// # Non-blocking functions
15
+
//
16
+
// Functions such as [Map], [Filter], and [Batch] take a stream as an input and return a new stream as an output.
17
+
// They do not block and return the output stream immediately. All the processing is done in the background by the goroutine pools they spawn.
18
+
// These functions forward all errors from the input stream to the output stream.
19
+
// Any errors returned by the user-provided functions are also sent to the output stream.
20
+
// When such function reaches the end of the input stream, it closes the output stream, stops processing and cleans up resources.
21
+
//
22
+
// Such functions are designed to be composed together to build complex processing pipelines:
23
+
//
24
+
// stage2 := rill.Map(input, ...)
25
+
// stage3 := rill.Batch(stage2, ...)
26
+
// stage4 := rill.Map(stage3, ...)
27
+
// results := rill.Unbatch(stage4, ...)
28
+
// // consume the results and handle errors with some blocking function
29
+
//
30
+
// # Blocking functions
31
+
//
32
+
// Functions such as [ForEach], [Reduce] and [MapReduce] are used at the last stage of the pipeline
33
+
// to consume the stream and return the final result or error.
34
+
//
35
+
// Usually, these functions block until one of the following conditions is met:
36
+
// - The end of the stream is reached. In this case, the function returns the final result.
37
+
// - An error is encountered either in the input stream or in some user-provided function. In this case, the function returns the error.
38
+
//
39
+
// In case of an early termination (before reaching the end of the input stream), such functions initiate
40
+
// background draining of the remaining items. This is done to prevent goroutine
41
+
// leaks by ensuring that all goroutines feeding the stream are allowed to complete.
42
+
// The input stream should not be used anymore after calling such functions.
43
+
//
44
+
// It's also possible to consume the pipeline results manually, for example using a for-range loop.
45
+
// In this case, add a deferred call to [DrainNB] before the loop to ensure that goroutines are not leaked.
46
+
//
47
+
// defer rill.DrainNB(results)
48
+
//
49
+
// for res := range results {
50
+
// if res.Error != nil {
51
+
// return res.Error
52
+
// }
53
+
// // process res.Value
54
+
// }
55
+
//
56
+
// # Unordered functions
57
+
//
58
+
// Functions such as [Map], [Filter] and [FlatMap] write items to the output stream as soon as they become available.
59
+
// Due to the concurrent nature of these functions, the order of items in the output stream may not match the order of items in the input stream.
60
+
// These functions prioritize performance and concurrency over maintaining the original order.
61
+
//
62
+
// # Ordered functions
63
+
//
64
+
// Functions such as [OrderedMap] or [OrderedFilter] preserve the order of items from the input stream.
65
+
// These functions are still concurrent, but use special synchronization techniques to ensure that
66
+
// items are written to the output stream in the same order as they were read from the input stream.
67
+
// This additional synchronization has some overhead, but it is negligible for i/o bound workloads.
68
+
//
69
+
// Some other functions, such as [ToSlice], [Batch] or [First] are not concurrent and are ordered by nature.
70
+
//
71
+
// # Error handling
72
+
//
73
+
// Error handling can be non-trivial in concurrent applications. Rill simplifies this by providing a structured error handling approach.
74
+
// As described above, all errors are automatically propagated down the pipeline to the final stage, where they can be caught.
75
+
// This allows the pipeline to terminate after the first error is encountered and return it to the caller.
76
+
//
77
+
// In cases where more complex error handling logic is required, the [Catch] function can be used.
78
+
// It allows to catch and handle errors at any point in the pipeline, providing the flexibility to handle not only the first error, but any of them.
0 commit comments