Skip to content

Commit

Permalink
Merge pull request #5 from roman/supervisor-supervisor
Browse files Browse the repository at this point in the history
Who Supervises the supervisor
  • Loading branch information
roman authored Feb 1, 2018
2 parents 26e34e7 + 655fee2 commit b0b473b
Show file tree
Hide file tree
Showing 45 changed files with 4,678 additions and 2,398 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
out/bin/*
out/profiled/*
tools/bin/*
tmp/*
target/*
.make/*
.tasty-rerun-log
**/.stack-work/*
**/.stack-work_*
*.cabal
/tutorial.md
.dir-locals.el
.dir-locals.el
9 changes: 2 additions & 7 deletions .tools.stack.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
resolver: lts-10.0
extra-deps:
- czipwith-1.0.0.0
- data-tree-print-0.1.0.0
- deque-0.2
- monad-memo-0.4.1
- butcher-1.2.1.0
resolver: lts-10.2
extra-deps: []
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,37 @@ The change log is available [on GitHub][2].
[1]: http://semver.org/spec/v2.0.0.html
[2]: https://github.com/roman/capataz/releases

## v0.1.0.0 Who supervises the supervisor?

**BREAKING CHANGES**

* Introduction of the `Process` type which is composed of both `Supervisor` and
`Worker` types
* Replace `defWorkerSpec` in favor of `workerSpec` and `workerSpecWithDefaults`
to build static workers
* Replace of `defWorkerOptions` in favor of `buildWorkerOptions` and
`buildWorkerOptionsWithDefaults` to build dynamic workers
* Replace `terminateWorker` in favor of `terminateProcess`
* Add `supervisorSpec`, `supervisorSpecWithDefaults` to build static supervision
trees
* Add `forkSupervisor`, `buildSupervisorOptions` and
`buildSupervisorOptionsWithDefaults` to build dynamic supervision trees
* Replace usage of default records semantics in favor of Lenses
* Add `joinCapatazThread` to avoid providing direct access to async of root
supervision tree
* Add `getSupervisorProcessId` to access the `ProcessId` of a given `Supervisor`
record (for dynamic termination)
* Add `getSupervisorAsync` to access the `Async ()` record of a supervisor
process thread
* Add `getCapatazTeardown` to access the `Teardown` record of the capataz system
* Move `CapatazEvent` records to new module `Control.Concurrent.Capataz.Event`
to avoid requiring `DuplicateRecordFields` extension on API users
* Remove `WorkerAction` alias as it is used for library development
documentation
* Add capataz-repo-watcher example to showcase static supervision trees
* Update capataz-simple-example unix-process example
* `forkCapataz` signature now requires name for root supervisor

## v0.0.0.2

* Bump bounds of `tasty` dependency
Expand Down
86 changes: 40 additions & 46 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,37 @@
################################################################################
## VARIABLE

PROJECT_NAME:=$(shell cat package.yaml | grep 'name:' | awk '{print $$2}')
PROJECT_VERSION:=$(shell cat package.yaml | grep -v '\#' | grep version | awk '{print $$2}' | sed -e "s;'\(.*\)';\1;")
RESOLVER ?= $(shell cat stack.yaml | grep -v '\#' | grep resolver | awk '{print $$2}')
PROJECT_SETUP_FILE=./.make/setup_done

FIND_HASKELL_SOURCES=find . -name "*.hs" -not -path '*.stack-work*'
HASKELL_FILES:=$(shell $(FIND_HASKELL_SOURCES) | grep 'src\|test')
FIND_HASKELL_FILES=find . -name "*.hs" -not -path '*.stack-work*'
HASKELL_FILES:=$(shell $(FIND_HASKELL_FILES) | grep 'src\|test')

BIN_DIR:=./out/bin
PROJECT_BIN_DIR:=./out/bin

STACK_DIST_DIR:=$(shell stack path --dist-dir)
SDIST_DIR_NAME:=capataz-$(PROJECT_VERSION)
INTERNAL_SDIST_TAR:=$(STACK_DIST_DIR)/$(SDIST_DIR_NAME).tar.gz
SDIST_DIR_NAME:=$(PROJECT_NAME)-$(PROJECT_VERSION)
INTERNAL_SDIST_TAR:=$(shell stack path --dist-dir)/$(SDIST_DIR_NAME).tar.gz
PROJECT_SDIST_TAR=target/$(SDIST_DIR_NAME).tar.gz

TOOLS_DIR=./tools/bin
BRITTANY_BIN:=$(TOOLS_DIR)/brittany
STYLISH_BIN:=$(TOOLS_DIR)/stylish-haskell
HLINT_BIN:=$(TOOLS_DIR)/hlint
PPSH_BIN:=$(TOOLS_DIR)/ppsh
PROJECT_TOOLS_DIR=./tools/bin
BRITTANY_BIN:=$(PROJECT_TOOLS_DIR)/brittany
STYLISH_BIN:=$(PROJECT_TOOLS_DIR)/stylish-haskell
HLINT_BIN:=$(PROJECT_TOOLS_DIR)/hlint
PPSH_BIN:=$(PROJECT_TOOLS_DIR)/ppsh
REFACTOR_BIN:=$(PROJECT_TOOLS_DIR)/refactor

EXAMPLE1_BIN=$(BIN_DIR)/example1
EXAMPLE2_BIN=$(BIN_DIR)/example2
EXAMPLE1_BIN=$(PROJECT_BIN_DIR)/example1
EXAMPLE2_BIN=$(PROJECT_BIN_DIR)/example2

BRITTANY=$(BRITTANY_BIN) --config-file .brittany.yml --write-mode inplace {} \;
STYLISH=$(STYLISH_BIN) -i {} \;
HLINT=$(HLINT_BIN) --refactor --refactor-options -i {} \;
BRITTANY_FIND_EXEC=$(BRITTANY_BIN) --config-file .brittany.yml --write-mode inplace {} \;
STYLISH_FIND_EXEC=$(STYLISH_BIN) -i {} \;
HLINT_FIND_EXEC=$(HLINT_BIN) --with-refactor=$$(pwd)/$(REFACTOR_BIN) --refactor --refactor-options -i {} \;

STACK:=stack --resolver $(RESOLVER) --install-ghc --local-bin-path ./target/bin
NIGHTLY_STACK:=stack --resolver nightly --install-ghc
TOOLS_STACK:=stack --stack-yaml .tools.stack.yaml --install-ghc --local-bin-path $(TOOLS_DIR)
TOOLS_STACK:=stack --stack-yaml .tools.stack.yaml --install-ghc --local-bin-path $(PROJECT_TOOLS_DIR)

################################################################################

Expand All @@ -43,29 +46,25 @@ help: ## Display this message

################################################################################

$(HLINT_BIN):
$(TOOLS_STACK) install hlint

$(STYLISH_BIN):
$(TOOLS_STACK) install stylish-haskell
$(EXAMPLE1_BIN): $(HASKELL_FILES)
$(STACK) build --copy-bins --local-bin-path $(PROJECT_BIN_DIR) --test --no-run-tests --haddock --no-haddock-deps --pedantic

$(BRITTANY_BIN):
$(TOOLS_STACK) install brittany
$(EXAMPLE2_BIN) : $(EXAMPLE1_BIN)

$(PPSH_BIN):
$(STACK) install pretty-show
$(INTERNAL_SDIST_TAR):
@mkdir -p target
$(NIGHTLY_STACK) sdist . --pvp-bounds both

$(EXAMPLE1_BIN): $(HASKELL_FILES)
$(STACK) build --copy-bins --local-bin-path $(BIN_DIR) --test --no-run-tests --haddock --no-haddock-deps --pedantic
$(PROJECT_SDIST_TAR): $(INTERNAL_SDIST_TAR)
cp $(INTERNAL_SDIST_TAR) target

$(EXAMPLE2_BIN) : $(EXAMPLE1_BIN)

.make/setup_done:
$(TOOLS_STACK) install hlint stylish-haskell pretty-show brittany refactor
$(PROJECT_SETUP_FILE):
$(TOOLS_STACK) install hlint stylish-haskell pretty-show brittany apply-refact
chmod -R go-w .stack-work
chmod go-w .ghci
@mkdir -p .make
@touch .make/setup_done
@touch $(PROJECT_SETUP_FILE)

################################################################################

Expand All @@ -74,12 +73,9 @@ build: $(EXAMPLE1_BIN) ## Build library and example binaries
test: $(EXAMPLE1_BIN) ## Execute test suites
$(STACK) test --dump-logs

sdist: clean ## Build a release
@mkdir -p target
$(NIGHTLY_STACK) sdist . --pvp-bounds both
cp $(INTERNAL_SDIST_TAR) target
sdist: $(PROJECT_SDIST_TAR) ## Build a release

untar-sdist: sdist
untar-sdist: $(INTERNAL_SDIST_TAR)
@mkdir -p tmp
tar xzf $(INTERNAL_SDIST_TAR)
@rm -rf tmp/$(SDIST_DIR_NAME) || true
Expand All @@ -88,24 +84,22 @@ untar-sdist: sdist
test-sdist: untar-sdist
cd tmp/$(SDIST_DIR_NAME) && $(NIGHTLY_STACK) init --force && $(NIGHTLY_STACK) build --test --bench --haddock --no-run-benchmarks

format: $(BRITTANY_BIN) $(STYLISH_BIN) ## Normalize style of source files
$(FIND_HASKELL_SOURCES) -exec $(BRITTANY) -exec $(STYLISH) && git diff --exit-code
format: $(PROJECT_SETUP_FILE) ## Normalize style of source files
$(FIND_HASKELL_FILES) -exec $(BRITTANY_FIND_EXEC) -exec $(STYLISH_FIND_EXEC) && git diff --exit-code

lint: $(HLINT_BIN) ## Execute linter
$(HLINT_BIN) $$($(FIND_HASKELL_SOURCES))
lint: $(PROJECT_SETUP_FILE) ## Execute linter
$(FIND_HASKELL_FILES) -exec $(HLINT_FIND_EXEC) && git diff --exit-code

repl: $(PPSH_BIN) ## Start project's repl
@chmod go-w -R .stack-work
@chmod go-w .ghci
repl: $(PROJECT_SETUP_FILE) ## Start project's repl
stack ghci

clean: ## Clean built artifacts
rm -f $(BIN_DIR)/*
rm -f $(PROJECT_BIN_DIR)/*
rm -f target/*
rm -rf tmp/*
stack clean

dev-setup: .make/setup_done ## Install development dependencies
dev-setup: $(PROJECT_SETUP_FILE) ## Install development dependencies

################################################################################
## Demo tasks
Expand Down
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* [Documentation](#documentation)
* [Development](#development)

## Raison d'etre
## Raison d'être

As time progresses, I've come to love developing concurrent applications in
Haskell, its API (STM, MVars, etc.) and light threading RTS bring a lot to the
Expand All @@ -29,16 +29,16 @@ provides a simple Supervisor API.
This library is intended to be a drop-in replacement to `forkIO` invocations
throughout your codebase, the difference being, you'll need to do a bit more of
setup specifying supervision rules, and also pass along a reference of a
capataz descriptor to every thread fork.
supervisor for every thread you fork.

### Why not [distributed-process](https://hackage.haskell.org/package/distributed-process)?

`distributed-process` is an impressive library, and brings many great utilities
if you need to develop applications that are reliable. However, it is a
heavyweight solution that will enforce serious changes to your application. It
also optimizes its implementation around the *distributed* part of its name.
This library is intended to provide some benefits of `distributed-process` ,
without the baggage.
if you need to develop applications that need to be distributed and reliable.
However, it is a heavyweight solution that will enforce serious changes to your
application. This library is intended to provide the reliability benefits of
`distributed-process`, without the constraints imposed by the *distributed*
part.

### Why not a complete actor system?

Expand Down Expand Up @@ -91,12 +91,15 @@ dependencies:
## Development

[![Build Status](https://travis-ci.org/roman/Haskell-capataz.svg?branch=master)](https://travis-ci.org/roman/Haskell-capataz)
[![Github](https://img.shields.io/github/commits-since/roman/haskell-capataz/v0.0.0.2.svg)](https://img.shields.io/github/commits-since/roman/haskell-capataz/v0.0.0.2.svg)
[![Github](https://img.shields.io/github/commits-since/roman/haskell-capataz/v0.1.0.0.svg)](https://img.shields.io/github/commits-since/roman/haskell-capataz/v0.1.0.0.svg)
[![Hackage Dependencies](https://img.shields.io/hackage-deps/v/capataz.svg)](http://packdeps.haskellers.com/feed?needle=capataz)

Follow the [developer guidelines](https://romanandreg.gitbooks.io/capataz/content/developer-guidelines.html)

## In next release
## In future releases

* Add support for supervising supervisors
* Replace Protolude in favor of RIO
* Documentation of performance analysis
* Documentation improvements
* capataz-dashboard package that provides web-ui with Supervisor statistics
* Ensure unit tests always finish on all concurrent scenarios (dejafu experiment)
21 changes: 7 additions & 14 deletions docs/developer-guidelines.md → docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,29 @@
# Developer Guidelines
[![Build Status](https://travis-ci.org/roman/Haskell-capataz.svg?branch=master)](https://travis-ci.org/roman/Haskell-capataz)
[![Github](https://img.shields.io/github/commits-since/roman/haskell-capataz/v0.0.0.2.svg)](https://img.shields.io/github/commits-since/roman/haskell-capataz/v0.0.0.2.svg)
[![Github](https://img.shields.io/github/commits-since/roman/haskell-capataz/v0.1.0.0.svg)](https://img.shields.io/github/commits-since/roman/haskell-capataz/v0.1.0.0.svg)
[![Hackage Dependencies](https://img.shields.io/hackage-deps/v/capataz.svg)](https://img.shields.io/hackage/v/capataz.svg)

## Dependencies

You'll need to install [Stack](https://github.com/commercialhaskell/stack), once installed, you can execute the `make` command.
You'll need to install [Stack](https://github.com/commercialhaskell/stack), once installed, you can execute the `make` command and learn tasks supported in the project.

You'll need to make sure you invoke `make format` and `make lint` when pushing changes, otherwise the Pull Request builder will fail.

## General Overview

This project heavily relies in two (2) Haskell extensions, [`NamedFieldPuns`]() and [`DuplicateRecordFields`]().
This project heavily relies in two (2) Haskell extensions, [`NamedFieldPuns`](https://downloads.haskell.org/~ghc/8.2.1/docs/html/users_guide/glasgow_exts.html#record-puns) and [`DuplicateRecordFields`](https://downloads.haskell.org/~ghc/8.2.1/docs/html/users_guide/glasgow_exts.html#duplicate-record-fields).

You can tell that _many_ records share the same fields, this is because these fields represent the same data in different contexts. This makes IMO the code more readable because we don't use different names (say, with a prefixes) to represent the same piece of information.
You can tell that many records share the same fields, this is because these fields represent the same data in different contexts. This makes IMO the code more readable because we don't use different names (say, with a prefixes) to represent the same piece of information.

However, this has the unfortunate side-effect that when using the field name as a function, we get ambiguous symbol errors from the compiler. To alliviate this, we only access the fields through _field pun_ notation.

Also, to avoid the requirement to use the `DuplicateRecordFields` extension on clients of the API, we provide lenses of the public API fields.

The code has been throughly documented, if you believe the documentation could be better, please create a ticket in Github with suggestions.

## Notes on testsuite

All tests related to this API are in a single module `Control.Concurrent.CapatazTest`, in this file we have defined:

* Assertion functions to get attributes from a `CapatazEvent`

* Helpers to run the test (reduce boilerplate)

* Actual tests

The module contains documentation for all the helper functions, and hopefully the test descriptions should be
enough to understand what is being tested.
The library is tested through integration tests that collect `CapatazEvent` from the system and assert they happen, this approach works great to avoid testing internal parts of the code that can change, however, the test-suite is not stable between executions because of timing.

If you have strong opinions about this testing approach, please reach out, I'm trying to validate if this is a good idea or not.

Expand Down
8 changes: 6 additions & 2 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
* Tutorials
* [Basic Tutorial using Unix Processes](tutorial.md)
* v0.0
* [Basic Tutorial using Unix Processes](v0.0/tutorial.md)
* v0.1
* [Unix Processes](v0.1/unix-process-tutorial.md)
* [Git Repository Synchronizer](v0.1/git-synchronizer-tutorial.md)

* Guide
* [Supervisor Theory](supervisor-theory.md)

* [Developer Guidelines](developer-guidelines.md)
* [Developer Guidelines](CONTRIBUTING.md)
1 change: 0 additions & 1 deletion docs/rationale.md

This file was deleted.

9 changes: 7 additions & 2 deletions docs/tutorial.md → docs/v0.0/tutorial.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Basic Tutorial using Unix Processes

> NOTE: This tutorial is for version v0.0.0.2 of the capataz library, follow
> [this
> link](https://romanandreg.gitbooks.io/capataz/content/v0.1/unix-process-tutorial.md)
> to read this tutorial with the newest version
In this tutorial, we will build a small CLI application that spawns processes through Haskell's Unix Process API. We will keep all our threads running smoothly despite having one of the threads killing the spawned Unix processes through Unix `SIGTERM` signals.

We will implement two (2) different versions of our CLI program, one using standard Haskell threading utilities, and another using the Capataz library.
Expand All @@ -17,7 +22,7 @@ If you are not familiar with the topics above, we recommend following along and

Our CLI program will receive an input parameter `procNumber` that will be used to spawn some green threads, each of them generating a Unix process. Each Unix process executes a simple bash script that echoes a number and increments it in an infinite while loop. Our Haskell program will also run a Haskell thread that will kill one of the many bash script executions.

You can find the code for this tutorial in the [`examples`](https://github.com/roman/Haskell-capataz/tree/examples/examples) directory of the project's Github repository.
You can find the code for this tutorial in the [`examples`](https://github.com/roman/Haskell-capataz/tree/v0.0.0.2/examples/capataz-example) directory of the project's Github repository.

## Setting up the stage - A trivial library for Processes

Expand Down Expand Up @@ -205,7 +210,7 @@ import Control.Concurrent.Capataz -- (0)
( WorkerOptions(..)
, CapatazOptions(..)
, WorkerRestartStrategy(..)
, CapatazRestartStrategy(..)
, SupervisorRestartStrategy(..)
, forkCapataz
, forkWorker
, defWorkerOptions
Expand Down
Loading

0 comments on commit b0b473b

Please sign in to comment.