Skip to content

Instantly share code, notes, and snippets.

@snnsnn
Forked from jcsherin/funsrv.md
Created December 19, 2024 11:23
Show Gist options
  • Select an option

  • Save snnsnn/592a66354fe53e36a8bec07011f42f1b to your computer and use it in GitHub Desktop.

Select an option

Save snnsnn/592a66354fe53e36a8bec07011f42f1b to your computer and use it in GitHub Desktop.

Revisions

  1. @jcsherin jcsherin revised this gist Apr 2, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -317,4 +317,6 @@ let service: Service.t(HttpRequest.t, HttpResponse.t) =

    # Conclusion

    The system is described declaratively using data flow and data dependencies. The data flow resembles a pipeline of functions which is statically type checked. There aren't too many moving parts here. There are just three abstractions - futures, services and filters. Everything is a function, which is easy to reason about in isolation. Services, and filters especially makes it easy for you to write your own middleware.


  2. @jcsherin jcsherin revised this gist Apr 2, 2020. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -12,7 +12,7 @@
    - [x] Challenges
    - [x] Finagle Ecosystem
    - [x] Check [notion notes](https://www.notion.so/Server-as-a-function-00f826f353014b7c81e1b143cd2b7918) for material which can improve this writing.
    - [x] Add TOC
    - [ ] Add TOC
    - [x] Remove these sections
    - [x] Interrupts
    - [x] Challenges
    @@ -316,3 +316,5 @@ let service: Service.t(HttpRequest.t, HttpResponse.t) =
    ```

    # Conclusion


  3. @jcsherin jcsherin revised this gist Apr 2, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -12,7 +12,7 @@
    - [x] Challenges
    - [x] Finagle Ecosystem
    - [x] Check [notion notes](https://www.notion.so/Server-as-a-function-00f826f353014b7c81e1b143cd2b7918) for material which can improve this writing.
    - [ ] Add TOC
    - [x] Add TOC
    - [x] Remove these sections
    - [x] Interrupts
    - [x] Challenges
  4. @jcsherin jcsherin revised this gist Apr 2, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -33,7 +33,7 @@
    6. [Services](#services)
    7. [Filters](#filters)
    1. [Composite Filters](#composite-filters)
    2. [Type Safety] (#type-safety)
    2. [Type Safety](#type-safety)
    8. [Conclusion](#conclusion)

    # Introduction
  5. @jcsherin jcsherin revised this gist Apr 2, 2020. 1 changed file with 15 additions and 6 deletions.
    21 changes: 15 additions & 6 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -21,11 +21,20 @@

    # TOC

    [Introduction](#introduction)

    [Servers & Clients](#servers--clients)

    [Abstractions](#abstractions)
    1. [Introduction](#introduction)
    2. [Servers & Clients](#servers--clients)
    3. [Abstractions](#abstractions)
    4. [Finagle Ecosystem](#finagle-ecosystem)
    5. [Futures](#futures)
    1. [Dependent Composition](#dependent-composition)
    2. [Handling Errors](#handling-errors)
    3. [Composing Multiple Dependencies](#composing-multiple-dependencies)
    4. [Recursive Composition](#recursive-composition)
    6. [Services](#services)
    7. [Filters](#filters)
    1. [Composite Filters](#composite-filters)
    2. [Type Safety] (#type-safety)
    8. [Conclusion](#conclusion)

    # Introduction

    @@ -209,7 +218,7 @@ let rec rsearch =
    };
    ```

    # Service
    # Services

    A service represents an endpoint to which requests are dispatched. It is a function which accepts a request parameter and returns a promise of a response. The service type `Service.t('req, 'resp)` is parametrized by the request type `'req` and the response type `'resp`.

  6. @jcsherin jcsherin revised this gist Apr 2, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -22,7 +22,9 @@
    # TOC

    [Introduction](#introduction)

    [Servers & Clients](#servers--clients)

    [Abstractions](#abstractions)

    # Introduction
  7. @jcsherin jcsherin revised this gist Apr 2, 2020. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -21,6 +21,10 @@

    # TOC

    [Introduction](#introduction)
    [Servers & Clients](#servers--clients)
    [Abstractions](#abstractions)

    # Introduction

    # Servers & Clients
  8. @jcsherin jcsherin revised this gist Mar 30, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -19,6 +19,8 @@

    [Your Server as a Function, Marius Eriksen](https://monkey.org/~marius/funsrv.pdf)

    # TOC

    # Introduction

    # Servers & Clients
  9. @jcsherin jcsherin revised this gist Mar 30, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@
    - [ ] Rewrite conclusion section.
    - [x] Include accidental vs essential complexity quote from out of the tarpit. It is already referenced in many places.
    - [x] Update code examples to use JS promises instead of `Future.t`.
    - [ ] Improve section on `andThen` combinator to improve comprehension (combine it with composite filters)
    - [x] Improve section on `andThen` combinator to improve comprehension (combine it with composite filters)
    - [x] Rewrite
    - [x] Composite Filter (move this within the filters section)
    - [x] Interrupts
  10. @jcsherin jcsherin revised this gist Mar 30, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -15,7 +15,7 @@
    - [ ] Add TOC
    - [x] Remove these sections
    - [x] Interrupts
    - [ ] Challenges
    - [x] Challenges

    [Your Server as a Function, Marius Eriksen](https://monkey.org/~marius/funsrv.pdf)

  11. @jcsherin jcsherin revised this gist Mar 30, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -14,7 +14,7 @@
    - [x] Check [notion notes](https://www.notion.so/Server-as-a-function-00f826f353014b7c81e1b143cd2b7918) for material which can improve this writing.
    - [ ] Add TOC
    - [x] Remove these sections
    - [ ] Interrupts
    - [x] Interrupts
    - [ ] Challenges

    [Your Server as a Function, Marius Eriksen](https://monkey.org/~marius/funsrv.pdf)
  12. @jcsherin jcsherin revised this gist Mar 30, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -13,7 +13,7 @@
    - [x] Finagle Ecosystem
    - [x] Check [notion notes](https://www.notion.so/Server-as-a-function-00f826f353014b7c81e1b143cd2b7918) for material which can improve this writing.
    - [ ] Add TOC
    - [ ] Remove these sections
    - [x] Remove these sections
    - [ ] Interrupts
    - [ ] Challenges

  13. @jcsherin jcsherin revised this gist Mar 30, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@
    - [x] Composite Filter (move this within the filters section)
    - [x] Interrupts
    - [x] Challenges
    - [ ] Finagle Ecosystem
    - [x] Finagle Ecosystem
    - [x] Check [notion notes](https://www.notion.so/Server-as-a-function-00f826f353014b7c81e1b143cd2b7918) for material which can improve this writing.
    - [ ] Add TOC
    - [ ] Remove these sections
  14. @jcsherin jcsherin revised this gist Mar 30, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,7 @@
    - [x] Rewrite
    - [x] Composite Filter (move this within the filters section)
    - [x] Interrupts
    - [ ] Challenges
    - [x] Challenges
    - [ ] Finagle Ecosystem
    - [x] Check [notion notes](https://www.notion.so/Server-as-a-function-00f826f353014b7c81e1b143cd2b7918) for material which can improve this writing.
    - [ ] Add TOC
  15. @jcsherin jcsherin revised this gist Mar 30, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -8,7 +8,7 @@
    - [ ] Improve section on `andThen` combinator to improve comprehension (combine it with composite filters)
    - [x] Rewrite
    - [x] Composite Filter (move this within the filters section)
    - [ ] Interrupts
    - [x] Interrupts
    - [ ] Challenges
    - [ ] Finagle Ecosystem
    - [x] Check [notion notes](https://www.notion.so/Server-as-a-function-00f826f353014b7c81e1b143cd2b7918) for material which can improve this writing.
  16. @jcsherin jcsherin revised this gist Mar 30, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@
    - [x] Include accidental vs essential complexity quote from out of the tarpit. It is already referenced in many places.
    - [x] Update code examples to use JS promises instead of `Future.t`.
    - [ ] Improve section on `andThen` combinator to improve comprehension (combine it with composite filters)
    - [ ] Rewrite
    - [x] Rewrite
    - [x] Composite Filter (move this within the filters section)
    - [ ] Interrupts
    - [ ] Challenges
  17. @jcsherin jcsherin revised this gist Mar 30, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@
    - [x] Update code examples to use JS promises instead of `Future.t`.
    - [ ] Improve section on `andThen` combinator to improve comprehension (combine it with composite filters)
    - [ ] Rewrite
    - [ ] Composite Filter (move this within the filters section)
    - [x] Composite Filter (move this within the filters section)
    - [ ] Interrupts
    - [ ] Challenges
    - [ ] Finagle Ecosystem
  18. @jcsherin jcsherin revised this gist Mar 30, 2020. 1 changed file with 1 addition and 24 deletions.
    25 changes: 1 addition & 24 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -298,27 +298,4 @@ let service: Service.t(HttpRequest.t, HttpResponse.t) =
    auth |> andThen(authedService);
    ```

    ### Interrupts

    A future is a read-only construct. The producer of a future value is separated from it's consumer. So the consumer of a future has no knowledge of their producers. Though a good abstraction this introduces resource leaking. This is similar to how Haskell with lazy evaluation semantics can introduce space leaks.

    The Future abstraction separates the producer of a value from it’s consumer. This is good for modularity, but not very efficient in using system resources. Say for some reason the a client doing network I/O is interrupted because the results are not needed anymore. The consumer has no way of notifying the Future because it’s a read-only value. So even though the value is not needed anymore, the producer continues performing the asynchronous operation. This is a form of resource leakage.

    Interrupts were added so that a consumer could notify the producer that the result is no longer needed. Interrupts do not directly change the state of the future, but a producer may act on it. They are advisory in nature.

    A control message was added to the RPC protocol which instructs the server to cancel all requests in progress. A client when interrupted issues this signal. When the cancellation signal is received by the server it raises an interrupt on the pending future. This allows to cancel all ongoing work on behalf of a request throughout the entire system.

    The semantics of cancelling / interrupting a future is not without ambiguities. The behaviour is undefined when Futures are composed using combinators. Should an interrupt propagate to all futures or only the outstanding ones? In cases where multiple consumers share the same underlying future, what should happen when only one of the clients are interrupted. So in practice they are limited to very few places in the runtime, in the low level network code.

    ### Challenges

    Futures, closures have deferred execution and are allocated to the garbage collected heap. The allocation footprint is non-trivial, and requires additional tooling for allocation analysis. Solid state devices provide very fast random access, but the need to periodically garbage collect large number of data blocks can increase read latency by a factor of 100 even with very modest level of write activity.

    In the case of the query segment example, the request is fanned-out to multiple servers using the collect combinator. The overall latency is dependent on the slowest server. With large request distribution involving 100s of systems it is common to encounter systems undergoing minor garbage collection in the request path.

    > Consider a system where each server typically responds in 10ms but with 99th percentile latency of one second. If a user request is handled on just a single server that means 1 user request in 100 will be slow taking 1 second. If a user request must collect responses from 100 such servers in parallel then 63% of user requests will take more than 1 second. Even for services with only one in 10,000 requests experiencing more than one second latency at the single server level, a service with 2000 such servers will see almost one in five user requests taking more than a second.
    [The Tail at Scale by Jeffrey Dean and Luiz André Barroso](https://www2.cs.duke.edu/courses/cps296.4/fall13/838-CloudPapers/dean_longtail.pdf)

    ## Conclusion

    Easier to reason about services with shared abstractions because everyone on other teams are writing in a similar style. So it becomes easier to isolate and reason about problems which happens at the boundaries of services, and fix them. It is also easy to attach statistics, tracing data, and any other runtime information using a common set of filters. This allows devops to export runtime information for monitoring and diagnosing the system without having to delve into specifics of the application logic.
    # Conclusion
  19. @jcsherin jcsherin revised this gist Mar 30, 2020. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -13,7 +13,10 @@
    - [ ] Finagle Ecosystem
    - [x] Check [notion notes](https://www.notion.so/Server-as-a-function-00f826f353014b7c81e1b143cd2b7918) for material which can improve this writing.
    - [ ] Add TOC

    - [ ] Remove these sections
    - [ ] Interrupts
    - [ ] Challenges

    [Your Server as a Function, Marius Eriksen](https://monkey.org/~marius/funsrv.pdf)

    # Introduction
  20. @jcsherin jcsherin revised this gist Mar 30, 2020. 1 changed file with 23 additions and 24 deletions.
    47 changes: 23 additions & 24 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -246,7 +246,9 @@ let timeoutFilter = (duration) =>
    (req, service) => req |> service |> within(duration)
    ```

    The `andThen` combinator can be used to combine filters with other filters to produce composite filters. Or it can be used with a service, producing a new service whose behaviour is modified by the filter. Because services are symmetric, filters can be applied to both clients and servers.
    ## Composite Filters

    The `andThen` combinator is useful for combining filters with other filters to produce composite filters. It can also be used with services to produce a new service whose behaviour is modified by the filter. Filters can be applied to both clients and servers.

    ```reason
    let httpClient: Service.t(HttpRequest.t, HttpResponse.t) = ...;
    @@ -255,23 +257,36 @@ let httpClientWithTimeout: Service.t(HttpRequest.t, HttpResponse.t) =
    timeoutFilter(seconds(10)) |> andThen(httpClient)
    ```

    ### Enforcing Type Safety
    > Filters have held their promise of providing clean, orthogonal, and application independent functionality. They are used universally: Finagle itself uses filters heavily; our frontend web servers—reverse HTTP proxies through which all of our external traffic flows - use a large stack of filters to implement different aspects of its responsibilities. This is an excerpt from its current configuration:
    >
    ```reason
    recordHandletime
    |> andThen(traceRequest)
    |> andThen(collectJvmStats)
    |> andThen(parseRequest)
    |> andThen(logRequest)
    |> andThen(recordClientStats)
    |> andThen(sanitize)
    |> andThen(respondToHealthCheck)
    |> andThen(applyTrafficControl)
    |> andThen(virtualHostServer);
    ```

    Filters can transform requests and responses. The programmer can avail the static typing of the compiler to enforce invariants. For example you can use a separate type for indicating authenticated HTTP requests.
    ## Type Safety

    The authReq function will authenticate the input request via an authentication service, and return an upgraded request on success, or fail to authenticate.
    Filters transform request and responses. The static typing of the compiler can be used to enforce certain guarantees. For example an HTTP server may use a separate type to indicate authenticated requests. The `authReq` function authenticates the input request via an auth service, and returns an upgraded request on success, or fails to authenticate.

    ```reason
    let authReq = (req: HttpRequest.t) => Future(AuthHttpRequest.t);
    let authReq = (req: HttpRequest.t) => Js.Promise.t(AuthHttpRequest.t);
    ```

    The auth filter composes with a service demanding authentication, to which unauthenticated requests can be dispatched.
    The `auth` filter can be composed with a service to upgrade it to a service which demands authentication. Unauthenticated requests can then be dispatched to this service for authentication.

    ```reason
    let auth = (req, service) => authReq(req) >>= service;
    let auth = (req, service) => authReq(req) |> Js.Promise.then_(service);
    ```

    If authentication fails the autheService is not accessed. If you try to write code composing an normal HTTP request type with authedService it will fail with a compilation error. This is good, as you get to catch errors early in the development cycle. You are using the compiler to protect from mixing up authenticated and unauthenticated requests. This is the awesome power of statically typed languages.
    Access is denied to `authedService` if authentication step fails. If you attempt to write code which provides an regular HTTP request type with `authedService` it is caught as a compilation error. The `authedService` expects an upgraded request of type `AuthHttpRequest.t`. You are able to enforce certain guarantees about your code availing the static typing abilities of the compiler. You can rest assured that you will not accidentally mix up between authenticated and unauthenticated requests.

    ```reason
    let authedService: Service.t(AuthHttpRequest.t, HttpResponse.t) = ...;
    @@ -280,22 +295,6 @@ let service: Service.t(HttpRequest.t, HttpResponse.t) =
    auth |> andThen(authedService);
    ```

    ## Composite Filter

    > Filters have held their promise of providing clean, orthogonal, and application-independent functionality. They are used universally: Finagle itself uses filters heavily; our frontend web servers—reverse HTTP proxies through which all of our external traffic flows use a large stack of filters to implement different aspects of its responsibilities. This is an excerpt from its current configuration:
    ```reason
    recordHandletime
    |> andThen(traceRequest)
    |> andThen(collectJvmStats)
    |> andThen(parseRequest)
    |> andThen(logRequest)
    |> andThen(recordClientStats)
    |> andThen(sanitize)
    |> andThen(respondToHealthCheck)
    |> andThen(applyTrafficControl)
    |> andThen(virtualHostServer);
    ```

    ### Interrupts

    A future is a read-only construct. The producer of a future value is separated from it's consumer. So the consumer of a future has no knowledge of their producers. Though a good abstraction this introduces resource leaking. This is similar to how Haskell with lazy evaluation semantics can introduce space leaks.
  21. @jcsherin jcsherin revised this gist Mar 29, 2020. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -12,6 +12,7 @@
    - [ ] Challenges
    - [ ] Finagle Ecosystem
    - [x] Check [notion notes](https://www.notion.so/Server-as-a-function-00f826f353014b7c81e1b143cd2b7918) for material which can improve this writing.
    - [ ] Add TOC

    [Your Server as a Function, Marius Eriksen](https://monkey.org/~marius/funsrv.pdf)

  22. @jcsherin jcsherin revised this gist Mar 29, 2020. 1 changed file with 8 additions and 8 deletions.
    16 changes: 8 additions & 8 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -62,7 +62,7 @@ __Futures__ are the same as a Promises in Javascript. __Services__ are transpare

    The code examples in the paper are written in [Scala](https://scala-lang.org/). They have been translated to [Reason](https://reasonml.github.io/). Reason lets us write type safe code, and has type inference. Values are immutable by default and have an inferred type. Values can be transformed using functions to the same or different types. Functions can be combined to form other higher order functions. Reason allows you to write code which is simple, and easy to reason about.

    ## Finagle Ecosystem
    # Finagle Ecosystem

    > Operations describe what is computed; execution is handled separately. This frees the programmer from attending to the minutiae of setting up threads, ensuring pools and queues are sized correctly, and making sure that resources are properly reclaimed — these concerns are instead handled by our runtime library, Finagle. Relinquishing the programmer from these responsibilities,the runtime is free to adapt to the situation at hand.
    >
    @@ -76,15 +76,15 @@ It became easier to reason about services which shared abstractions because mult

    Easier to reason about services with shared abstractions because everyone on other teams are writing in a similar style. So it becomes easier to isolate and reason about problems which happens at the boundaries of services, and fix them. Possible to turn on stats for a service which helps with debugging by instrumenting the service. Service itself is transparent as it could be leither local or something which goes over the network.

    ## Futures
    # Futures

    When this paper was written Scala did not have a Promise implementation. In fact very few mainstream programming languages had an impelmentation. Today the terms future, promise, delay, deferred and eventual all refer to the same concept in multiple languages.

    > A future is a container used to hold the result of an asynchronous operation such as a network RPC, a timeout, or a disk I/O operation. A future is either __empty__ -— the result is not yet available; __succeeded__ -— the producer has completed and has populated the future with the result of the operation; or __failed__ -— the producer failed, and the future contains the resulting exception.
    ![Futures](https://i.imgur.com/KwFUKAwl.png)

    ### Dependent Composition
    ## Dependent Composition

    This examples demonstrates sequencing two asynchronous operations which have a data dependency. For example a search engine frontend, to provide personalized results will rewrite the query. The type of __Futures__ or a promise in Reason is represented by `Js.Promise.t`.

    @@ -112,7 +112,7 @@ let personalizedSearch = (~user, ~query) =>
    personalizedSearch(~user="alice", ~query="where is bob?");
    ```

    ### Handling Errors
    ## Handling Errors

    When the outer `rewrite` in `personalizedSearch` fails it short circuits computation. The dependent `search` function is not executed. A failed promise contains an exception. Since the promise contains no value, the `Js.Promise.then_` is not executed.

    @@ -145,7 +145,7 @@ let personalizedSearch = (~user, ~query) =>

    If the results of `rewrite` is not available within 50ms the outer promise will fail. On a `TimeoutError` we degrade to using the original query parameter for search with `Js.Promise.resolve(query)`.

    ### Composing Multiple Dependencies
    ## Composing Multiple Dependencies

    When you need to issue multiple requests and aggregate the results, the `collect` combinator is provided for resolving multiple data dependencies. The Reason equivalent is the `Js.Promise.all` function.

    @@ -174,7 +174,7 @@ let search = (~query) =>
    |> Js.Promise.resolve)
    ```

    ### Recursive Composition
    ## Recursive Composition

    This is an example of searching iteratively until we have the required number of results by permuting the query in some manner in each iteration. Tail call optimization should be implemented to avoid stack size exceeded runtime errors.

    @@ -197,7 +197,7 @@ let rec rsearch =
    };
    ```

    ### Service
    # Service

    A service represents an endpoint to which requests are dispatched. It is a function which accepts a request parameter and returns a promise of a response. The service type `Service.t('req, 'resp)` is parametrized by the request type `'req` and the response type `'resp`.

    @@ -228,7 +228,7 @@ Http.serve(":80", (req: HttpRequest.t) =>
    Http.serve(":8080", Http.newService("twitter.com:80"));
    ```

    ### Filters
    # Filters

    Filters implement application agnostic concerns such as timeouts, retries, metrics, authentication etc. They are composed with services to modify service behaviour. Its type is a function which accepts a request, and service as its parameters and returns a promise of a response.

  23. @jcsherin jcsherin revised this gist Mar 29, 2020. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -5,9 +5,9 @@
    - [ ] Rewrite conclusion section.
    - [x] Include accidental vs essential complexity quote from out of the tarpit. It is already referenced in many places.
    - [x] Update code examples to use JS promises instead of `Future.t`.
    - [ ] Improve section on `andThen` combinator to improve comprehension.
    - [ ] Improve section on `andThen` combinator to improve comprehension (combine it with composite filters)
    - [ ] Rewrite
    - [ ] Composite Filter
    - [ ] Composite Filter (move this within the filters section)
    - [ ] Interrupts
    - [ ] Challenges
    - [ ] Finagle Ecosystem
  24. @jcsherin jcsherin revised this gist Mar 29, 2020. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -230,11 +230,11 @@ Http.serve(":8080", Http.newService("twitter.com:80"));

    ### Filters

    A number of application agnostic concerns have to be handled when building servers and clients. Such as handling timeouts, retry policies, statistics, authentication etc. The filter function implements application independent functionality. They can be composed with a request and a service to modify their behaviour.
    Filters implement application agnostic concerns such as timeouts, retries, metrics, authentication etc. They are composed with services to modify service behaviour. Its type is a function which accepts a request, and service as its parameters and returns a promise of a response.

    ```reason
    module Filter = {
    type t('req, 'resp) = ('req, Service.t('req, 'resp)) => Future.t('resp);
    type t('req, 'resp) = ('req, Service.t('req, 'resp)) => Js.Promise.t('resp);
    };
    ```

  25. @jcsherin jcsherin revised this gist Mar 29, 2020. 1 changed file with 6 additions and 6 deletions.
    12 changes: 6 additions & 6 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -74,7 +74,7 @@ Twitter wanted to move to Scala, and their systems had to continue to work with

    It became easier to reason about services which shared abstractions because multiple teams were all writing code in the same style. This really improved the ability to isolate and reason about issues which happened at the boundaries of services, and fix them fast.

    Easier to reason about services with shared abstractions because everyone on other teams are writing in a similar style. So it becomes easier to isolate and reason about problems which happens at the boundaries of services, and fix them. Possible to turn on stats for a service which helps with debugging by instrumenting the service. Service itself is transparent as it could be leither ocal or something which goes over the network.
    Easier to reason about services with shared abstractions because everyone on other teams are writing in a similar style. So it becomes easier to isolate and reason about problems which happens at the boundaries of services, and fix them. Possible to turn on stats for a service which helps with debugging by instrumenting the service. Service itself is transparent as it could be leither local or something which goes over the network.

    ## Futures

    @@ -199,29 +199,29 @@ let rec rsearch =

    ### Service

    A service is an asynchronous function which accepts a request as it’s only parameter and returns a future of a response value. It typically represents an endpoint to which RPCs are dispatched.
    A service represents an endpoint to which requests are dispatched. It is a function which accepts a request parameter and returns a promise of a response. The service type `Service.t('req, 'resp)` is parametrized by the request type `'req` and the response type `'resp`.

    ```reason
    module Service = {
    type t('req, 'resp) = 'req => Future.t('resp);
    type t('req, 'resp) = 'req => Js.Promise.t('resp);
    };
    ```

    Services represents clients and servers symmetrically. This example shows making an HTTP request from a client to the Twitter website.
    Clients and servers are symmetric in their operation. The __Services__ abstraction represents both clients and servers. The next example shows making an HTTP request to the Twitter website.

    ```reason
    let client: Service.t(HttpRequest.t, HttpResponse.t) =
    Http.newService("twitter.com:80");
    let getIndex: Future.t(HttpResponse.t) = HttpRequest.make("/") |> client;
    let getIndex: Js.Promise.t(HttpResponse.t) = HttpRequest.make("/") |> client;
    ```

    These are examples of an echo server and, a primitive HTTP proxy forwarding local traffic from port 8080 to twitter.com.

    ```reason
    /* An HTTP Echo server */
    Http.serve(":80", (req: HttpRequest.t) =>
    req |> HttpRequest.body |> HttpResponse.make(Status.OK) |> Future.value
    req |> HttpRequest.body |> HttpResponse.make(Status.OK) |> Js.Promise.resolve
    );
    /* Primitive HTTP proxy */
  26. @jcsherin jcsherin revised this gist Mar 29, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -179,7 +179,7 @@ let search = (~query) =>
    This is an example of searching iteratively until we have the required number of results by permuting the query in some manner in each iteration. Tail call optimization should be implemented to avoid stack size exceeded runtime errors.

    ```reason
    let rsearch =
    let rec rsearch =
    (user, query, results, n: int): Js.Promise.t(array(SearchResult.t)) =>
    if (Array.length(results) >= n) {
    Js.Promise.resolve(results);
  27. @jcsherin jcsherin revised this gist Mar 29, 2020. 1 changed file with 7 additions and 10 deletions.
    17 changes: 7 additions & 10 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -174,27 +174,24 @@ let search = (~query) =>
    |> Js.Promise.resolve)
    ```

    ### Recurisve Composition
    ### Recursive Composition

    This is an example of searching iteratively until we have the required number of results by permuting the query in some way in each iteration.

    Future combinators can also be composed recursively. Tail call elimination should be implemented in code and supported by the underlying runtime so that you do not see dreaded stack size exceeded errors. With tail call elimination this is not going to leak memory, and you do not have to worry about the depth of recursion.
    This is an example of searching iteratively until we have the required number of results by permuting the query in some manner in each iteration. Tail call optimization should be implemented to avoid stack size exceeded runtime errors.

    ```reason
    let rsearch =
    (user, query, results, n: int): Future.t(list(SearchResult.t)) =>
    if (List.length(results) >= n) {
    Future.value(results);
    (user, query, results, n: int): Js.Promise.t(array(SearchResult.t)) =>
    if (Array.length(results) >= n) {
    Js.Promise.resolve(results);
    } else {
    let nextQuery = permute(~query);
    personalizedSearch(~user, ~query=nextQuery)
    >>= (
    newResults =>
    |> Js.Promise.then_(newResults =>
    if (List.length(newResults) > 0) {
    rsearch(user, nextQuery,(results @ newResults), n);
    } else {
    Future.value(results);
    Js.Promise.resolve(results);
    }
    );
    };
  28. @jcsherin jcsherin revised this gist Mar 29, 2020. 1 changed file with 14 additions and 9 deletions.
    23 changes: 14 additions & 9 deletions funsrv.md
    Original file line number Diff line number Diff line change
    @@ -145,28 +145,33 @@ let personalizedSearch = (~user, ~query) =>

    If the results of `rewrite` is not available within 50ms the outer promise will fail. On a `TimeoutError` we degrade to using the original query parameter for search with `Js.Promise.resolve(query)`.

    ### Concurrent Composition
    ### Composing Multiple Dependencies

    When you want to perform a scatter-gather or fan-out query to multiple downstream servers, you are concurrently executing futures. The collect combinator resolves multiple dependencies. For some type ‘a, collect converts a list of Futures of type ‘a into a future whose value is a list of type ‘a. We use collect to resolve multiple dependencies.
    When you need to issue multiple requests and aggregate the results, the `collect` combinator is provided for resolving multiple data dependencies. The Reason equivalent is the `Js.Promise.all` function.

    ```reason
    let collect: list(Future.t('a)) => Future.t(list('a));
    Js.Promise.all: array(Js.Promise.t('a)) => Js.Promise.t(array('a))
    ```

    Query segment returns a future who value is a list of search results.
    This example is for a search frontend which splits requests to a replica of each segment, and then combines the results. The `querySegment` function returns an array of search results for segment identified by `id`.

    ```reason
    let querySegment:
    (~id: int, ~query: string) => Future.t(list(SearchResult.t));
    (~id: int, ~query: string) => Js.Promise.t(array(SearchResult.t));
    ```

    We can dispatch query segment to ‘numSegments’ servers downstream. Now we have a collection of futures which returns search results. The collect combinator resolves them concurrently. If there is a failure in any of the returned futures, the collected future will fail immediately. This behaviour is similar to Javascript Promise.all.
    The `Js.Promise.all` can be used to split queries to a replica of each segment concurrently. If any of the promises fail, the collected future will fail immediately.

    ```reason
    let search = (~query) =>
    List.makeBy(numSegments, i => querySegment(i, query))
    |> collect
    >>= (results => results |> List.flatten |> Future.value);
    Js.Promise.all([|
    querySegment(~id=0, ~query),
    querySegment(~id=1, ~query),
    querySegment(~id=2, ~query)|])
    |> Js.Promise.then_(results =>
    results
    |> Array.flatten
    |> Js.Promise.resolve)
    ```

    ### Recurisve Composition
  29. @jcsherin jcsherin revised this gist Mar 29, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@
    - [ ] Add an introduction section which sets the context for this narrative. Building a modern web framework which can apply the principles used in building Finagle. Such a web framework written a statically typed language can provide safety guarantees at compile time. The functional style emphasizing immutability, composition, isolation of side effects etc improves our ability to reason about behaviour.
    - [ ] Rewrite conclusion section.
    - [x] Include accidental vs essential complexity quote from out of the tarpit. It is already referenced in many places.
    - [ ] Update code examples to use JS promises instead of `Future.t`.
    - [x] Update code examples to use JS promises instead of `Future.t`.
    - [ ] Improve section on `andThen` combinator to improve comprehension.
    - [ ] Rewrite
    - [ ] Composite Filter
  30. @jcsherin jcsherin revised this gist Mar 29, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion funsrv.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    - [x] Side by side terminology of Future and JS Promises to show similarity. This could be setup earlier when Futures abstraction is introduced. The code examples can then instead use JS Promises which should be familiar to frontend programmers.
    - [x] References to Finagle are ad-hoc at best in the current draft. Make it clear to the reader how Finagle is relevant to the paper under discussion, and the goals of the project.
    - [ ] Fix signature difference between Future.t and Js.Promise.t in code examples
    - [x] Fix signature difference between Future.t and Js.Promise.t in code examples
    - [ ] Add an introduction section which sets the context for this narrative. Building a modern web framework which can apply the principles used in building Finagle. Such a web framework written a statically typed language can provide safety guarantees at compile time. The functional style emphasizing immutability, composition, isolation of side effects etc improves our ability to reason about behaviour.
    - [ ] Rewrite conclusion section.
    - [x] Include accidental vs essential complexity quote from out of the tarpit. It is already referenced in many places.