Skip to content

Repository files navigation

Parapipe - paralleling pipeline

Mentioned in Awesome GotestslinterscoverageGo Report CardGoDoc

The library provides a zero-dependency non-blocking buffered FIFO-pipeline for structuring the code and vertically scaling your app. Unlike regular pipeline examples you may find on the internet - parapipe executes everything on each step concurrently, yet maintaining the output order. Although, this library does not use any locks or mutexes. Just pure channels.

When to use

  • processed data can be divided in chunks (messages), and the flow may consist of one or more stages
  • data should be processed concurrently (scaled vertically)
  • the order of processing messages must be maintained

Installation

go get -u github.com/nazar256/parapipe@latest

Usage

  1. Create a pipeline with first step. Processing callback is generic (so as the pipeline). It may receive and return any type of data, but the second return value should always be a boolean.
concurrency:=runtime.NumCPU() // how many messages to process concurrently for each pipepipeline:=parapipe.NewPipeline(concurrency, func(msgYourInputType) (YourOutputType, bool) {
// do something and generate a new value "someValue"shouldProceedWithNextStep:=truereturnsomeValue, shouldProceedWithNextStep
})
  1. Add pipes - call Attach() function one or more times to add steps to the pipeline
p1:=parapipe.NewPipeline(runtime.NumCPU(), func(msgint) (int, bool) {
time.Sleep(30*time.Millisecond)
returnmsg+1000, true
})
p2:=parapipe.Attach(p1, parapipe.NewPipeline(concurrency, func(msgint) (string, bool) {
time.Sleep(30*time.Millisecond)
returnstrconv.Itoa(msg), true
}))
// final pipeline you are going to work with (push messages and read output)pipeline:=parapipe.Attach(p2, parapipe.NewPipeline(concurrency, func(msgstring) (string, bool) {
time.Sleep(30*time.Millisecond)
return"#"+msg, true
}))
  1. Get "out" channel when all pipes are added and read results from it
forresult:=rangepipeline.Out() {
// do something with the result
}

It's important to drain the pipeline (read everything from "out") even when the pipeline won't produce any viable result. It could be stuck otherwise.

  1. Push values for processing into the pipeline:
pipeline.Push("something")
  1. Close pipeline to after the last message. This will cleanup its resources and close its output channel. It's not recommended closing pipeline using defer because you may not want to hang output util defer is executed.
pipeline.Close()

Circuit breaking

In some cases (errors) there could be impossible to process a message, thus there is no way to pass it further. In such case just return false as a second return value from the step processing callback. The first value will be ignored.

pipeline.Pipe(4, func(inputValueInputType) (OutputType, bool) {
someValue, err:=someOperation(inputValue)
iferr!=nil {
// handle the error// slog.Error("error when calling someOperation", "err", err)returnsomeValue, false
}
returnsomeValue, true
})
// ...forresult:=rangepipeline.Out() {
// do something with the result
}

Performance

Parapipe makes use of generics and channels. Overall it should be performant enough for most of the cases. It has zero heap allocations in hot code, thus generates little load for garbage collector. However, it uses channels under the hood and is bottlenecked mostly by the channel operations which are several writes and reads per each message.

Examples

AMQP middleware

Parapipe can be handful when you need to process messages in the middle concurrently, yet maintaining their order.

See the working example of using parapipe in AMQP client.

Other examples

With parapipe you can:

  • in your API respond a long JSON-feed as stream, retrieve, enrich and marshal each object concurrently, in maintained order and return them to the client
  • fetch and merge entries from different sources as one stream
  • structure your API controllers or handlers
  • processing heavy files in effective way

About

Paralleling pipeline

Resources

Stars

38 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - nazar256/parapipe: Paralleling pipeline · GitHub
Skip to content

Repository files navigation

Parapipe - paralleling pipeline

Mentioned in Awesome GotestslinterscoverageGo Report CardGoDoc

The library provides a zero-dependency non-blocking buffered FIFO-pipeline for structuring the code and vertically scaling your app. Unlike regular pipeline examples you may find on the internet - parapipe executes everything on each step concurrently, yet maintaining the output order. Although, this library does not use any locks or mutexes. Just pure channels.

When to use

  • processed data can be divided in chunks (messages), and the flow may consist of one or more stages
  • data should be processed concurrently (scaled vertically)
  • the order of processing messages must be maintained

Installation

go get -u github.com/nazar256/parapipe@latest

Usage

  1. Create a pipeline with first step. Processing callback is generic (so as the pipeline). It may receive and return any type of data, but the second return value should always be a boolean.
concurrency:=runtime.NumCPU() // how many messages to process concurrently for each pipepipeline:=parapipe.NewPipeline(concurrency, func(msgYourInputType) (YourOutputType, bool) {
// do something and generate a new value "someValue"shouldProceedWithNextStep:=truereturnsomeValue, shouldProceedWithNextStep
})
  1. Add pipes - call Attach() function one or more times to add steps to the pipeline
p1:=parapipe.NewPipeline(runtime.NumCPU(), func(msgint) (int, bool) {
time.Sleep(30*time.Millisecond)
returnmsg+1000, true
})
p2:=parapipe.Attach(p1, parapipe.NewPipeline(concurrency, func(msgint) (string, bool) {
time.Sleep(30*time.Millisecond)
returnstrconv.Itoa(msg), true
}))
// final pipeline you are going to work with (push messages and read output)pipeline:=parapipe.Attach(p2, parapipe.NewPipeline(concurrency, func(msgstring) (string, bool) {
time.Sleep(30*time.Millisecond)
return"#"+msg, true
}))
  1. Get "out" channel when all pipes are added and read results from it
forresult:=rangepipeline.Out() {
// do something with the result
}

It's important to drain the pipeline (read everything from "out") even when the pipeline won't produce any viable result. It could be stuck otherwise.

  1. Push values for processing into the pipeline:
pipeline.Push("something")
  1. Close pipeline to after the last message. This will cleanup its resources and close its output channel. It's not recommended closing pipeline using defer because you may not want to hang output util defer is executed.
pipeline.Close()

Circuit breaking

In some cases (errors) there could be impossible to process a message, thus there is no way to pass it further. In such case just return false as a second return value from the step processing callback. The first value will be ignored.

pipeline.Pipe(4, func(inputValueInputType) (OutputType, bool) {
someValue, err:=someOperation(inputValue)
iferr!=nil {
// handle the error// slog.Error("error when calling someOperation", "err", err)returnsomeValue, false
}
returnsomeValue, true
})
// ...forresult:=rangepipeline.Out() {
// do something with the result
}

Performance

Parapipe makes use of generics and channels. Overall it should be performant enough for most of the cases. It has zero heap allocations in hot code, thus generates little load for garbage collector. However, it uses channels under the hood and is bottlenecked mostly by the channel operations which are several writes and reads per each message.

Examples

AMQP middleware

Parapipe can be handful when you need to process messages in the middle concurrently, yet maintaining their order.

See the working example of using parapipe in AMQP client.

Other examples

With parapipe you can:

  • in your API respond a long JSON-feed as stream, retrieve, enrich and marshal each object concurrently, in maintained order and return them to the client
  • fetch and merge entries from different sources as one stream
  • structure your API controllers or handlers
  • processing heavy files in effective way

About

Paralleling pipeline

Resources

Stars

38 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - nazar256/parapipe: Paralleling pipeline · GitHub
Skip to content

Repository files navigation

Parapipe - paralleling pipeline

Mentioned in Awesome GotestslinterscoverageGo Report CardGoDoc

The library provides a zero-dependency non-blocking buffered FIFO-pipeline for structuring the code and vertically scaling your app. Unlike regular pipeline examples you may find on the internet - parapipe executes everything on each step concurrently, yet maintaining the output order. Although, this library does not use any locks or mutexes. Just pure channels.

When to use

  • processed data can be divided in chunks (messages), and the flow may consist of one or more stages
  • data should be processed concurrently (scaled vertically)
  • the order of processing messages must be maintained

Installation

go get -u github.com/nazar256/parapipe@latest

Usage

  1. Create a pipeline with first step. Processing callback is generic (so as the pipeline). It may receive and return any type of data, but the second return value should always be a boolean.
concurrency:=runtime.NumCPU() // how many messages to process concurrently for each pipepipeline:=parapipe.NewPipeline(concurrency, func(msgYourInputType) (YourOutputType, bool) {
// do something and generate a new value "someValue"shouldProceedWithNextStep:=truereturnsomeValue, shouldProceedWithNextStep
})
  1. Add pipes - call Attach() function one or more times to add steps to the pipeline
p1:=parapipe.NewPipeline(runtime.NumCPU(), func(msgint) (int, bool) {
time.Sleep(30*time.Millisecond)
returnmsg+1000, true
})
p2:=parapipe.Attach(p1, parapipe.NewPipeline(concurrency, func(msgint) (string, bool) {
time.Sleep(30*time.Millisecond)
returnstrconv.Itoa(msg), true
}))
// final pipeline you are going to work with (push messages and read output)pipeline:=parapipe.Attach(p2, parapipe.NewPipeline(concurrency, func(msgstring) (string, bool) {
time.Sleep(30*time.Millisecond)
return"#"+msg, true
}))
  1. Get "out" channel when all pipes are added and read results from it
forresult:=rangepipeline.Out() {
// do something with the result
}

It's important to drain the pipeline (read everything from "out") even when the pipeline won't produce any viable result. It could be stuck otherwise.

  1. Push values for processing into the pipeline:
pipeline.Push("something")
  1. Close pipeline to after the last message. This will cleanup its resources and close its output channel. It's not recommended closing pipeline using defer because you may not want to hang output util defer is executed.
pipeline.Close()

Circuit breaking

In some cases (errors) there could be impossible to process a message, thus there is no way to pass it further. In such case just return false as a second return value from the step processing callback. The first value will be ignored.

pipeline.Pipe(4, func(inputValueInputType) (OutputType, bool) {
someValue, err:=someOperation(inputValue)
iferr!=nil {
// handle the error// slog.Error("error when calling someOperation", "err", err)returnsomeValue, false
}
returnsomeValue, true
})
// ...forresult:=rangepipeline.Out() {
// do something with the result
}

Performance

Parapipe makes use of generics and channels. Overall it should be performant enough for most of the cases. It has zero heap allocations in hot code, thus generates little load for garbage collector. However, it uses channels under the hood and is bottlenecked mostly by the channel operations which are several writes and reads per each message.

Examples

AMQP middleware

Parapipe can be handful when you need to process messages in the middle concurrently, yet maintaining their order.

See the working example of using parapipe in AMQP client.

Other examples

With parapipe you can:

  • in your API respond a long JSON-feed as stream, retrieve, enrich and marshal each object concurrently, in maintained order and return them to the client
  • fetch and merge entries from different sources as one stream
  • structure your API controllers or handlers
  • processing heavy files in effective way

About

Paralleling pipeline

Resources

Stars

38 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - nazar256/parapipe: Paralleling pipeline · GitHub
Skip to content

Repository files navigation

Parapipe - paralleling pipeline

Mentioned in Awesome GotestslinterscoverageGo Report CardGoDoc

The library provides a zero-dependency non-blocking buffered FIFO-pipeline for structuring the code and vertically scaling your app. Unlike regular pipeline examples you may find on the internet - parapipe executes everything on each step concurrently, yet maintaining the output order. Although, this library does not use any locks or mutexes. Just pure channels.

When to use

  • processed data can be divided in chunks (messages), and the flow may consist of one or more stages
  • data should be processed concurrently (scaled vertically)
  • the order of processing messages must be maintained

Installation

go get -u github.com/nazar256/parapipe@latest

Usage

  1. Create a pipeline with first step. Processing callback is generic (so as the pipeline). It may receive and return any type of data, but the second return value should always be a boolean.
concurrency:=runtime.NumCPU() // how many messages to process concurrently for each pipepipeline:=parapipe.NewPipeline(concurrency, func(msgYourInputType) (YourOutputType, bool) {
// do something and generate a new value "someValue"shouldProceedWithNextStep:=truereturnsomeValue, shouldProceedWithNextStep
})
  1. Add pipes - call Attach() function one or more times to add steps to the pipeline
p1:=parapipe.NewPipeline(runtime.NumCPU(), func(msgint) (int, bool) {
time.Sleep(30*time.Millisecond)
returnmsg+1000, true
})
p2:=parapipe.Attach(p1, parapipe.NewPipeline(concurrency, func(msgint) (string, bool) {
time.Sleep(30*time.Millisecond)
returnstrconv.Itoa(msg), true
}))
// final pipeline you are going to work with (push messages and read output)pipeline:=parapipe.Attach(p2, parapipe.NewPipeline(concurrency, func(msgstring) (string, bool) {
time.Sleep(30*time.Millisecond)
return"#"+msg, true
}))
  1. Get "out" channel when all pipes are added and read results from it
forresult:=rangepipeline.Out() {
// do something with the result
}

It's important to drain the pipeline (read everything from "out") even when the pipeline won't produce any viable result. It could be stuck otherwise.

  1. Push values for processing into the pipeline:
pipeline.Push("something")
  1. Close pipeline to after the last message. This will cleanup its resources and close its output channel. It's not recommended closing pipeline using defer because you may not want to hang output util defer is executed.
pipeline.Close()

Circuit breaking

In some cases (errors) there could be impossible to process a message, thus there is no way to pass it further. In such case just return false as a second return value from the step processing callback. The first value will be ignored.

pipeline.Pipe(4, func(inputValueInputType) (OutputType, bool) {
someValue, err:=someOperation(inputValue)
iferr!=nil {
// handle the error// slog.Error("error when calling someOperation", "err", err)returnsomeValue, false
}
returnsomeValue, true
})
// ...forresult:=rangepipeline.Out() {
// do something with the result
}

Performance

Parapipe makes use of generics and channels. Overall it should be performant enough for most of the cases. It has zero heap allocations in hot code, thus generates little load for garbage collector. However, it uses channels under the hood and is bottlenecked mostly by the channel operations which are several writes and reads per each message.

Examples

AMQP middleware

Parapipe can be handful when you need to process messages in the middle concurrently, yet maintaining their order.

See the working example of using parapipe in AMQP client.

Other examples

With parapipe you can:

  • in your API respond a long JSON-feed as stream, retrieve, enrich and marshal each object concurrently, in maintained order and return them to the client
  • fetch and merge entries from different sources as one stream
  • structure your API controllers or handlers
  • processing heavy files in effective way

About

Paralleling pipeline

Resources

Stars

38 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - nazar256/parapipe: Paralleling pipeline · GitHub
Skip to content

Repository files navigation

Parapipe - paralleling pipeline

Mentioned in Awesome GotestslinterscoverageGo Report CardGoDoc

The library provides a zero-dependency non-blocking buffered FIFO-pipeline for structuring the code and vertically scaling your app. Unlike regular pipeline examples you may find on the internet - parapipe executes everything on each step concurrently, yet maintaining the output order. Although, this library does not use any locks or mutexes. Just pure channels.

When to use

  • processed data can be divided in chunks (messages), and the flow may consist of one or more stages
  • data should be processed concurrently (scaled vertically)
  • the order of processing messages must be maintained

Installation

go get -u github.com/nazar256/parapipe@latest

Usage

  1. Create a pipeline with first step. Processing callback is generic (so as the pipeline). It may receive and return any type of data, but the second return value should always be a boolean.
concurrency:=runtime.NumCPU() // how many messages to process concurrently for each pipepipeline:=parapipe.NewPipeline(concurrency, func(msgYourInputType) (YourOutputType, bool) {
// do something and generate a new value "someValue"shouldProceedWithNextStep:=truereturnsomeValue, shouldProceedWithNextStep
})
  1. Add pipes - call Attach() function one or more times to add steps to the pipeline
p1:=parapipe.NewPipeline(runtime.NumCPU(), func(msgint) (int, bool) {
time.Sleep(30*time.Millisecond)
returnmsg+1000, true
})
p2:=parapipe.Attach(p1, parapipe.NewPipeline(concurrency, func(msgint) (string, bool) {
time.Sleep(30*time.Millisecond)
returnstrconv.Itoa(msg), true
}))
// final pipeline you are going to work with (push messages and read output)pipeline:=parapipe.Attach(p2, parapipe.NewPipeline(concurrency, func(msgstring) (string, bool) {
time.Sleep(30*time.Millisecond)
return"#"+msg, true
}))
  1. Get "out" channel when all pipes are added and read results from it
forresult:=rangepipeline.Out() {
// do something with the result
}

It's important to drain the pipeline (read everything from "out") even when the pipeline won't produce any viable result. It could be stuck otherwise.

  1. Push values for processing into the pipeline:
pipeline.Push("something")
  1. Close pipeline to after the last message. This will cleanup its resources and close its output channel. It's not recommended closing pipeline using defer because you may not want to hang output util defer is executed.
pipeline.Close()

Circuit breaking

In some cases (errors) there could be impossible to process a message, thus there is no way to pass it further. In such case just return false as a second return value from the step processing callback. The first value will be ignored.

pipeline.Pipe(4, func(inputValueInputType) (OutputType, bool) {
someValue, err:=someOperation(inputValue)
iferr!=nil {
// handle the error// slog.Error("error when calling someOperation", "err", err)returnsomeValue, false
}
returnsomeValue, true
})
// ...forresult:=rangepipeline.Out() {
// do something with the result
}

Performance

Parapipe makes use of generics and channels. Overall it should be performant enough for most of the cases. It has zero heap allocations in hot code, thus generates little load for garbage collector. However, it uses channels under the hood and is bottlenecked mostly by the channel operations which are several writes and reads per each message.

Examples

AMQP middleware

Parapipe can be handful when you need to process messages in the middle concurrently, yet maintaining their order.

See the working example of using parapipe in AMQP client.

Other examples

With parapipe you can:

  • in your API respond a long JSON-feed as stream, retrieve, enrich and marshal each object concurrently, in maintained order and return them to the client
  • fetch and merge entries from different sources as one stream
  • structure your API controllers or handlers
  • processing heavy files in effective way

About

Paralleling pipeline

Resources

Stars

38 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - nazar256/parapipe: Paralleling pipeline · GitHub
Skip to content

Repository files navigation

Parapipe - paralleling pipeline

Mentioned in Awesome GotestslinterscoverageGo Report CardGoDoc

The library provides a zero-dependency non-blocking buffered FIFO-pipeline for structuring the code and vertically scaling your app. Unlike regular pipeline examples you may find on the internet - parapipe executes everything on each step concurrently, yet maintaining the output order. Although, this library does not use any locks or mutexes. Just pure channels.

When to use

  • processed data can be divided in chunks (messages), and the flow may consist of one or more stages
  • data should be processed concurrently (scaled vertically)
  • the order of processing messages must be maintained

Installation

go get -u github.com/nazar256/parapipe@latest

Usage

  1. Create a pipeline with first step. Processing callback is generic (so as the pipeline). It may receive and return any type of data, but the second return value should always be a boolean.
concurrency:=runtime.NumCPU() // how many messages to process concurrently for each pipepipeline:=parapipe.NewPipeline(concurrency, func(msgYourInputType) (YourOutputType, bool) {
// do something and generate a new value "someValue"shouldProceedWithNextStep:=truereturnsomeValue, shouldProceedWithNextStep
})
  1. Add pipes - call Attach() function one or more times to add steps to the pipeline
p1:=parapipe.NewPipeline(runtime.NumCPU(), func(msgint) (int, bool) {
time.Sleep(30*time.Millisecond)
returnmsg+1000, true
})
p2:=parapipe.Attach(p1, parapipe.NewPipeline(concurrency, func(msgint) (string, bool) {
time.Sleep(30*time.Millisecond)
returnstrconv.Itoa(msg), true
}))
// final pipeline you are going to work with (push messages and read output)pipeline:=parapipe.Attach(p2, parapipe.NewPipeline(concurrency, func(msgstring) (string, bool) {
time.Sleep(30*time.Millisecond)
return"#"+msg, true
}))
  1. Get "out" channel when all pipes are added and read results from it
forresult:=rangepipeline.Out() {
// do something with the result
}

It's important to drain the pipeline (read everything from "out") even when the pipeline won't produce any viable result. It could be stuck otherwise.

  1. Push values for processing into the pipeline:
pipeline.Push("something")
  1. Close pipeline to after the last message. This will cleanup its resources and close its output channel. It's not recommended closing pipeline using defer because you may not want to hang output util defer is executed.
pipeline.Close()

Circuit breaking

In some cases (errors) there could be impossible to process a message, thus there is no way to pass it further. In such case just return false as a second return value from the step processing callback. The first value will be ignored.

pipeline.Pipe(4, func(inputValueInputType) (OutputType, bool) {
someValue, err:=someOperation(inputValue)
iferr!=nil {
// handle the error// slog.Error("error when calling someOperation", "err", err)returnsomeValue, false
}
returnsomeValue, true
})
// ...forresult:=rangepipeline.Out() {
// do something with the result
}

Performance

Parapipe makes use of generics and channels. Overall it should be performant enough for most of the cases. It has zero heap allocations in hot code, thus generates little load for garbage collector. However, it uses channels under the hood and is bottlenecked mostly by the channel operations which are several writes and reads per each message.

Examples

AMQP middleware

Parapipe can be handful when you need to process messages in the middle concurrently, yet maintaining their order.

See the working example of using parapipe in AMQP client.

Other examples

With parapipe you can:

  • in your API respond a long JSON-feed as stream, retrieve, enrich and marshal each object concurrently, in maintained order and return them to the client
  • fetch and merge entries from different sources as one stream
  • structure your API controllers or handlers
  • processing heavy files in effective way

About

Paralleling pipeline

Resources

Stars

38 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - nazar256/parapipe: Paralleling pipeline · GitHub
Skip to content

Repository files navigation

Parapipe - paralleling pipeline

Mentioned in Awesome GotestslinterscoverageGo Report CardGoDoc

The library provides a zero-dependency non-blocking buffered FIFO-pipeline for structuring the code and vertically scaling your app. Unlike regular pipeline examples you may find on the internet - parapipe executes everything on each step concurrently, yet maintaining the output order. Although, this library does not use any locks or mutexes. Just pure channels.

When to use

  • processed data can be divided in chunks (messages), and the flow may consist of one or more stages
  • data should be processed concurrently (scaled vertically)
  • the order of processing messages must be maintained

Installation

go get -u github.com/nazar256/parapipe@latest

Usage

  1. Create a pipeline with first step. Processing callback is generic (so as the pipeline). It may receive and return any type of data, but the second return value should always be a boolean.
concurrency:=runtime.NumCPU() // how many messages to process concurrently for each pipepipeline:=parapipe.NewPipeline(concurrency, func(msgYourInputType) (YourOutputType, bool) {
// do something and generate a new value "someValue"shouldProceedWithNextStep:=truereturnsomeValue, shouldProceedWithNextStep
})
  1. Add pipes - call Attach() function one or more times to add steps to the pipeline
p1:=parapipe.NewPipeline(runtime.NumCPU(), func(msgint) (int, bool) {
time.Sleep(30*time.Millisecond)
returnmsg+1000, true
})
p2:=parapipe.Attach(p1, parapipe.NewPipeline(concurrency, func(msgint) (string, bool) {
time.Sleep(30*time.Millisecond)
returnstrconv.Itoa(msg), true
}))
// final pipeline you are going to work with (push messages and read output)pipeline:=parapipe.Attach(p2, parapipe.NewPipeline(concurrency, func(msgstring) (string, bool) {
time.Sleep(30*time.Millisecond)
return"#"+msg, true
}))
  1. Get "out" channel when all pipes are added and read results from it
forresult:=rangepipeline.Out() {
// do something with the result
}

It's important to drain the pipeline (read everything from "out") even when the pipeline won't produce any viable result. It could be stuck otherwise.

  1. Push values for processing into the pipeline:
pipeline.Push("something")
  1. Close pipeline to after the last message. This will cleanup its resources and close its output channel. It's not recommended closing pipeline using defer because you may not want to hang output util defer is executed.
pipeline.Close()

Circuit breaking

In some cases (errors) there could be impossible to process a message, thus there is no way to pass it further. In such case just return false as a second return value from the step processing callback. The first value will be ignored.

pipeline.Pipe(4, func(inputValueInputType) (OutputType, bool) {
someValue, err:=someOperation(inputValue)
iferr!=nil {
// handle the error// slog.Error("error when calling someOperation", "err", err)returnsomeValue, false
}
returnsomeValue, true
})
// ...forresult:=rangepipeline.Out() {
// do something with the result
}

Performance

Parapipe makes use of generics and channels. Overall it should be performant enough for most of the cases. It has zero heap allocations in hot code, thus generates little load for garbage collector. However, it uses channels under the hood and is bottlenecked mostly by the channel operations which are several writes and reads per each message.

Examples

AMQP middleware

Parapipe can be handful when you need to process messages in the middle concurrently, yet maintaining their order.

See the working example of using parapipe in AMQP client.

Other examples

With parapipe you can:

  • in your API respond a long JSON-feed as stream, retrieve, enrich and marshal each object concurrently, in maintained order and return them to the client
  • fetch and merge entries from different sources as one stream
  • structure your API controllers or handlers
  • processing heavy files in effective way

About

Paralleling pipeline

Resources

Stars

38 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - nazar256/parapipe: Paralleling pipeline · GitHub
Skip to content

Repository files navigation

Parapipe - paralleling pipeline

Mentioned in Awesome GotestslinterscoverageGo Report CardGoDoc

The library provides a zero-dependency non-blocking buffered FIFO-pipeline for structuring the code and vertically scaling your app. Unlike regular pipeline examples you may find on the internet - parapipe executes everything on each step concurrently, yet maintaining the output order. Although, this library does not use any locks or mutexes. Just pure channels.

When to use

  • processed data can be divided in chunks (messages), and the flow may consist of one or more stages
  • data should be processed concurrently (scaled vertically)
  • the order of processing messages must be maintained

Installation

go get -u github.com/nazar256/parapipe@latest

Usage

  1. Create a pipeline with first step. Processing callback is generic (so as the pipeline). It may receive and return any type of data, but the second return value should always be a boolean.
concurrency:=runtime.NumCPU() // how many messages to process concurrently for each pipepipeline:=parapipe.NewPipeline(concurrency, func(msgYourInputType) (YourOutputType, bool) {
// do something and generate a new value "someValue"shouldProceedWithNextStep:=truereturnsomeValue, shouldProceedWithNextStep
})
  1. Add pipes - call Attach() function one or more times to add steps to the pipeline
p1:=parapipe.NewPipeline(runtime.NumCPU(), func(msgint) (int, bool) {
time.Sleep(30*time.Millisecond)
returnmsg+1000, true
})
p2:=parapipe.Attach(p1, parapipe.NewPipeline(concurrency, func(msgint) (string, bool) {
time.Sleep(30*time.Millisecond)
returnstrconv.Itoa(msg), true
}))
// final pipeline you are going to work with (push messages and read output)pipeline:=parapipe.Attach(p2, parapipe.NewPipeline(concurrency, func(msgstring) (string, bool) {
time.Sleep(30*time.Millisecond)
return"#"+msg, true
}))
  1. Get "out" channel when all pipes are added and read results from it
forresult:=rangepipeline.Out() {
// do something with the result
}

It's important to drain the pipeline (read everything from "out") even when the pipeline won't produce any viable result. It could be stuck otherwise.

  1. Push values for processing into the pipeline:
pipeline.Push("something")
  1. Close pipeline to after the last message. This will cleanup its resources and close its output channel. It's not recommended closing pipeline using defer because you may not want to hang output util defer is executed.
pipeline.Close()

Circuit breaking

In some cases (errors) there could be impossible to process a message, thus there is no way to pass it further. In such case just return false as a second return value from the step processing callback. The first value will be ignored.

pipeline.Pipe(4, func(inputValueInputType) (OutputType, bool) {
someValue, err:=someOperation(inputValue)
iferr!=nil {
// handle the error// slog.Error("error when calling someOperation", "err", err)returnsomeValue, false
}
returnsomeValue, true
})
// ...forresult:=rangepipeline.Out() {
// do something with the result
}

Performance

Parapipe makes use of generics and channels. Overall it should be performant enough for most of the cases. It has zero heap allocations in hot code, thus generates little load for garbage collector. However, it uses channels under the hood and is bottlenecked mostly by the channel operations which are several writes and reads per each message.

Examples

AMQP middleware

Parapipe can be handful when you need to process messages in the middle concurrently, yet maintaining their order.

See the working example of using parapipe in AMQP client.

Other examples

With parapipe you can:

  • in your API respond a long JSON-feed as stream, retrieve, enrich and marshal each object concurrently, in maintained order and return them to the client
  • fetch and merge entries from different sources as one stream
  • structure your API controllers or handlers
  • processing heavy files in effective way

About

Paralleling pipeline

Resources

Stars

38 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages