This commit is contained in:
Jonathan Goldstein 2018-12-11 22:33:08 -08:00
Родитель a9d45898a7 419fa6c57c
Коммит 65e782c29e
24 изменённых файлов: 723 добавлений и 222 удалений

Просмотреть файл

@ -1,25 +1,37 @@
language: minimal
language: csharp
mono: none
dotnet: 2.1
dist: xenial
services:
- docker
addons:
apt:
packages:
- libunwind-dev
- make
- gcc
env:
global:
# Mount the logs from outside the container:
# Mount the logs from outside the container when/if running PerformanceTestInterruptible:
- PTI_MOUNT_LOGS=ExternalLogs
matrix:
matrix:
# Bring up a basic test within or between containers:
- PTI_MODE=OneContainer
- PTI_MODE=TwoContainers
- DOCK=nodocker
- DOCK=docker PTI_MODE=OneContainer
# - DOCK=docker PTI_MODE=TwoContainers
before_install:
- sudo apt-get install -y libunwind-dev make gcc
script:
# Need to remove the dependence on Azure Tables /
# AZURE_STORAGE_CONN_STRING if we want to do full CI in a public
# context (or find some way to use an account without leaking its auth
# info).
# - ./._run_ci.sh
# In the meantime, we can just make sure the images build:
- ./build_docker_images.sh
# In the meantime, this will just make sure that everything builds.
- ./Scripts/run_linux_ci.sh $DOCK

284
Architecture.svg Normal file
Просмотреть файл

@ -0,0 +1,284 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by Microsoft Visio, SVG Export Architecture.svg Page-1 -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="8.3941in" height="5.44479in"
viewBox="0 0 604.375 392.025" xml:space="preserve" color-interpolation-filters="sRGB" class="st13">
<v:documentProperties v:langID="1033" v:viewMarkup="false">
<v:userDefs>
<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
</v:userDefs>
</v:documentProperties>
<style type="text/css">
<![CDATA[
.st1 {fill:#ffffff}
.st2 {stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
.st3 {fill:#ffffff;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2}
.st4 {fill:#000000;font-family:Calibri;font-size:1.99999em}
.st5 {font-size:1em}
.st6 {fill:#ffffff;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st7 {fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2}
.st8 {marker-end:url(#mrkr5-73);marker-start:url(#mrkr5-70);stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2}
.st9 {fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:0.44247787610619}
.st10 {fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.25}
.st11 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
.st12 {fill:#000000;font-family:Calibri;font-size:1.99999em;font-weight:bold}
.st13 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
]]>
</style>
<defs id="Markers">
<g id="lend5">
<path d="M 2 1 L 0 0 L 1.98117 -0.993387 C 1.67173 -0.364515 1.67301 0.372641 1.98465 1.00043 " style="stroke:none"/>
</g>
<marker id="mrkr5-70" class="st9" v:arrowType="5" v:arrowSize="2" v:setback="3.775" refX="3.775" orient="auto"
markerUnits="strokeWidth" overflow="visible">
<use xlink:href="#lend5" transform="scale(2.26) "/>
</marker>
<marker id="mrkr5-73" class="st9" v:arrowType="5" v:arrowSize="2" v:setback="3.955" refX="-3.955" orient="auto"
markerUnits="strokeWidth" overflow="visible">
<use xlink:href="#lend5" transform="scale(-2.26,-2.26) "/>
</marker>
</defs>
<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
<title>Page-1</title>
<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="19" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
<v:layer v:name="Flowchart" v:index="0"/>
<v:layer v:name="Connector" v:index="1"/>
<g id="group1-1" transform="translate(18.75,-144.625)" v:mID="1" v:groupContext="group">
<title>Sheet.1</title>
<g id="shape2-2" v:mID="2" v:groupContext="shape" v:layerMember="0">
<title>Disk storage</title>
<v:custProps>
<v:cp v:nameU="Cost" v:lbl="Cost" v:type="7" v:format="@" v:langID="1033"/>
<v:cp v:nameU="Duration" v:lbl="Duration" v:type="2" v:langID="1033"/>
<v:cp v:nameU="Resources" v:lbl="Resources" v:langID="1033"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<path d="M0 345.22 L0 384.82 A36 7.2 -180 1 0 72 384.82 L72 345.22 A36 7.2 -180 0 0 0 345.22 Z" class="st1"/>
<path d="M0 345.22 L0 384.82 A36 7.2 -180 1 0 72 384.82 L72 345.22 A36 7.2 -180 0 0 0 345.22" class="st2"/>
<path d="M0 345.22 A36 7.2 -180 1 0 72 345.22" class="st2"/>
<path d="M0 348.82 A36 7.2 -180 1 0 72 348.82" class="st2"/>
</g>
<g id="shape3-7" v:mID="3" v:groupContext="shape" v:layerMember="0" transform="translate(18,0)">
<title>Disk storage.6</title>
<v:custProps>
<v:cp v:nameU="Cost" v:lbl="Cost" v:type="7" v:format="@" v:langID="1033"/>
<v:cp v:nameU="Duration" v:lbl="Duration" v:type="2" v:langID="1033"/>
<v:cp v:nameU="Resources" v:lbl="Resources" v:langID="1033"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<path d="M0 345.22 L0 384.82 A36 7.2 -180 1 0 72 384.82 L72 345.22 A36 7.2 -180 0 0 0 345.22 Z" class="st1"/>
<path d="M0 345.22 L0 384.82 A36 7.2 -180 1 0 72 384.82 L72 345.22 A36 7.2 -180 0 0 0 345.22" class="st2"/>
<path d="M0 345.22 A36 7.2 -180 1 0 72 345.22" class="st2"/>
<path d="M0 348.82 A36 7.2 -180 1 0 72 348.82" class="st2"/>
</g>
<g id="shape4-12" v:mID="4" v:groupContext="shape" v:layerMember="0" transform="translate(36,0)">
<title>Disk storage.7</title>
<v:custProps>
<v:cp v:nameU="Cost" v:lbl="Cost" v:type="7" v:format="@" v:langID="1033"/>
<v:cp v:nameU="Duration" v:lbl="Duration" v:type="2" v:langID="1033"/>
<v:cp v:nameU="Resources" v:lbl="Resources" v:langID="1033"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<path d="M0 345.22 L0 384.82 A36 7.2 -180 1 0 72 384.82 L72 345.22 A36 7.2 -180 0 0 0 345.22 Z" class="st1"/>
<path d="M0 345.22 L0 384.82 A36 7.2 -180 1 0 72 384.82 L72 345.22 A36 7.2 -180 0 0 0 345.22" class="st2"/>
<path d="M0 345.22 A36 7.2 -180 1 0 72 345.22" class="st2"/>
<path d="M0 348.82 A36 7.2 -180 1 0 72 348.82" class="st2"/>
</g>
</g>
<g id="shape5-17" v:mID="5" v:groupContext="shape" transform="translate(144.75,-252.625)">
<title>Rectangle.42</title>
<desc>Immortal Coordinator</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)"/>
<v:textRect cx="67.5" cy="356.025" width="135" height="72"/>
<rect x="0" y="320.025" width="135" height="72" class="st3"/>
<text x="22.27" y="348.82" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Immortal <tspan
x="8.62" dy="1.2em" class="st5">Coordinator</tspan></text> </g>
<g id="group6-21" transform="translate(315.75,-144.625)" v:mID="6" v:groupContext="group">
<title>Sheet.6</title>
<g id="shape7-22" v:mID="7" v:groupContext="shape" v:layerMember="0">
<title>Disk storage</title>
<v:custProps>
<v:cp v:nameU="Cost" v:lbl="Cost" v:type="7" v:format="@" v:langID="1033"/>
<v:cp v:nameU="Duration" v:lbl="Duration" v:type="2" v:langID="1033"/>
<v:cp v:nameU="Resources" v:lbl="Resources" v:langID="1033"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<path d="M0 345.22 L0 384.82 A36 7.2 -180 1 0 72 384.82 L72 345.22 A36 7.2 -180 0 0 0 345.22 Z" class="st1"/>
<path d="M0 345.22 L0 384.82 A36 7.2 -180 1 0 72 384.82 L72 345.22 A36 7.2 -180 0 0 0 345.22" class="st2"/>
<path d="M0 345.22 A36 7.2 -180 1 0 72 345.22" class="st2"/>
<path d="M0 348.82 A36 7.2 -180 1 0 72 348.82" class="st2"/>
</g>
<g id="shape8-27" v:mID="8" v:groupContext="shape" v:layerMember="0" transform="translate(18,0)">
<title>Disk storage.6</title>
<v:custProps>
<v:cp v:nameU="Cost" v:lbl="Cost" v:type="7" v:format="@" v:langID="1033"/>
<v:cp v:nameU="Duration" v:lbl="Duration" v:type="2" v:langID="1033"/>
<v:cp v:nameU="Resources" v:lbl="Resources" v:langID="1033"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<path d="M0 345.22 L0 384.82 A36 7.2 -180 1 0 72 384.82 L72 345.22 A36 7.2 -180 0 0 0 345.22 Z" class="st1"/>
<path d="M0 345.22 L0 384.82 A36 7.2 -180 1 0 72 384.82 L72 345.22 A36 7.2 -180 0 0 0 345.22" class="st2"/>
<path d="M0 345.22 A36 7.2 -180 1 0 72 345.22" class="st2"/>
<path d="M0 348.82 A36 7.2 -180 1 0 72 348.82" class="st2"/>
</g>
<g id="shape9-32" v:mID="9" v:groupContext="shape" v:layerMember="0" transform="translate(36,0)">
<title>Disk storage.7</title>
<v:custProps>
<v:cp v:nameU="Cost" v:lbl="Cost" v:type="7" v:format="@" v:langID="1033"/>
<v:cp v:nameU="Duration" v:lbl="Duration" v:type="2" v:langID="1033"/>
<v:cp v:nameU="Resources" v:lbl="Resources" v:langID="1033"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<path d="M0 345.22 L0 384.82 A36 7.2 -180 1 0 72 384.82 L72 345.22 A36 7.2 -180 0 0 0 345.22 Z" class="st1"/>
<path d="M0 345.22 L0 384.82 A36 7.2 -180 1 0 72 384.82 L72 345.22 A36 7.2 -180 0 0 0 345.22" class="st2"/>
<path d="M0 345.22 A36 7.2 -180 1 0 72 345.22" class="st2"/>
<path d="M0 348.82 A36 7.2 -180 1 0 72 348.82" class="st2"/>
</g>
</g>
<g id="shape10-37" v:mID="10" v:groupContext="shape" transform="translate(441.75,-252.625)">
<title>Rectangle.50</title>
<desc>Immortal Coordinator</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)"/>
<v:textRect cx="67.5" cy="356.025" width="135" height="72"/>
<rect x="0" y="320.025" width="135" height="72" class="st3"/>
<text x="22.27" y="348.82" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Immortal <tspan
x="8.62" dy="1.2em" class="st5">Coordinator</tspan></text> </g>
<g id="group11-41" transform="translate(151.5,-30.625)" v:mID="11" v:groupContext="group">
<title>Sheet.11</title>
<g id="group12-42" transform="translate(0,-1.5)" v:mID="12" v:groupContext="group">
<title>Sheet.12</title>
<g id="shape13-43" v:mID="13" v:groupContext="shape">
<title>Rectangle.48</title>
<desc>Application</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)"/>
<v:textRect cx="60.75" cy="371.775" width="121.5" height="40.5"/>
<rect x="0" y="351.525" width="121.5" height="40.5" class="st6"/>
<text x="5.46" y="378.97" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Application</text> </g>
<g id="shape14-46" v:mID="14" v:groupContext="shape" transform="translate(0,-40.5)">
<title>Rectangle.51</title>
<desc>AMBROSIA Binding</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)"/>
<v:textRect cx="60.75" cy="365.025" width="121.5" height="54"/>
<rect x="0" y="338.025" width="121.5" height="54" class="st6"/>
<text x="7.08" y="357.82" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>AMBROSIA <tspan
x="24.15" dy="1.2em" class="st5">Binding</tspan></text> </g>
</g>
<g id="shape15-50" v:mID="15" v:groupContext="shape">
<title>Rectangle.54</title>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<rect x="0" y="296.025" width="121.5" height="96" class="st7"/>
</g>
</g>
<g id="group16-52" transform="translate(448.5,-30.625)" v:mID="16" v:groupContext="group">
<title>Sheet.16</title>
<g id="group17-53" transform="translate(0,-1.5)" v:mID="17" v:groupContext="group">
<title>Sheet.17</title>
<g id="shape18-54" v:mID="18" v:groupContext="shape">
<title>Rectangle.48</title>
<desc>Application</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)"/>
<v:textRect cx="60.75" cy="371.775" width="121.5" height="40.5"/>
<rect x="0" y="351.525" width="121.5" height="40.5" class="st6"/>
<text x="5.46" y="378.97" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Application</text> </g>
<g id="shape19-57" v:mID="19" v:groupContext="shape" transform="translate(0,-40.5)">
<title>Rectangle.51</title>
<desc>AMBROSIA Binding</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)"/>
<v:textRect cx="60.75" cy="365.025" width="121.5" height="54"/>
<rect x="0" y="338.025" width="121.5" height="54" class="st6"/>
<text x="7.08" y="357.82" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>AMBROSIA <tspan
x="24.15" dy="1.2em" class="st5">Binding</tspan></text> </g>
</g>
<g id="shape20-61" v:mID="20" v:groupContext="shape">
<title>Rectangle.54</title>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<rect x="0" y="296.025" width="121.5" height="96" class="st7"/>
</g>
</g>
<g id="shape21-63" v:mID="21" v:groupContext="shape" v:layerMember="1" transform="translate(203.25,-252.625)">
<title>Dynamic connector.61</title>
<path d="M9 399.57 L9 399.93 L9 510.11" class="st8"/>
</g>
<g id="shape22-74" v:mID="22" v:groupContext="shape" v:layerMember="1" transform="translate(279.75,-279.625)">
<title>Dynamic connector.63</title>
<path d="M7.55 383.02 L7.91 383.02 L154.09 383.02" class="st8"/>
</g>
<g id="shape23-81" v:mID="23" v:groupContext="shape" v:layerMember="1" transform="translate(500.25,-252.625)">
<title>Dynamic connector.64</title>
<path d="M9 399.57 L9 399.93 L9 510.11" class="st8"/>
</g>
<g id="shape24-88" v:mID="24" v:groupContext="shape" v:layerMember="1" transform="translate(144.75,-252.625)">
<title>Dynamic connector.66</title>
<path d="M-7.55 392.02 L-7.91 392.02 L-72 392.02 L-72 438.11" class="st8"/>
</g>
<g id="shape25-95" v:mID="25" v:groupContext="shape" v:layerMember="1" transform="translate(441.75,-252.625)">
<title>Dynamic connector.68</title>
<path d="M-7.55 392.02 L-7.91 392.02 L-54 392.02 L-54 438.11" class="st8"/>
</g>
<g id="shape26-102" v:mID="26" v:groupContext="shape" transform="translate(135.75,-18.625)">
<title>Rectangle.71</title>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<rect x="0" y="68.0247" width="153" height="324" class="st10"/>
</g>
<g id="shape27-104" v:mID="27" v:groupContext="shape" transform="translate(432.75,-18.625)">
<title>Rectangle.72</title>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<rect x="0" y="68.0247" width="153" height="324" class="st10"/>
</g>
<g id="shape28-106" v:mID="28" v:groupContext="shape" transform="translate(136.75,-349.625)">
<title>Sheet.28</title>
<desc>Immortal 1</desc>
<v:textBlock v:margins="rect(4,4,4,4)"/>
<v:textRect cx="76" cy="382.025" width="152" height="20"/>
<rect x="0" y="372.025" width="152" height="20" class="st11"/>
<text x="20.73" y="389.22" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Immortal 1</text> </g>
<g id="shape29-109" v:mID="29" v:groupContext="shape" transform="translate(432.75,-349.625)">
<title>Sheet.29</title>
<desc>Immortal 2</desc>
<v:textBlock v:margins="rect(4,4,4,4)"/>
<v:textRect cx="76" cy="382.025" width="152" height="20"/>
<rect x="0" y="372.025" width="152" height="20" class="st11"/>
<text x="20.73" y="389.22" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Immortal 2</text> </g>
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 15 KiB

3
CONTRIBUTING.md Normal file
Просмотреть файл

@ -0,0 +1,3 @@
# Contributing
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

Просмотреть файл

@ -2,37 +2,62 @@
Client Protocol for AMBROSIA network participants
=================================================
This document covers how a network endpoint should communicate with
the AMBROSIA reliability coordinator assigned to it (typically located
within the same physical machine).
This document covers how an application should communicate with the AMBROSIA
reliability coordinator assigned to it. The coordinator is located within the
same physical machine/container and assumed to survive or fail with the
application process. The coordinator communicates via TCP/IP over a local
socket with the application. This process separation is designed to minimize
assumptions about the application and maximize language-agnosticity.
Overview and Terminology
------------------------
FINISHME
In AMBROSIA a set of application processes (services) serve as communication
endpoints, communicating *exclusively* through the network of Immortal
Coordinators, which collectively serve as the message bus. The individual
processes (or objects contained therein) are the *Immortals* which survive the
failure of individual machines.
* Commit ID
* Sequence ID
Below we use the following terminology:
* "Async/await" RPC - a classic notion of a *future*. These RPCs return a value back to the caller. Because AMBROSIA ensures reliability, they are semantically identical to function calls, without introducing new failure modes such as timeouts or disconnections.
* Committer ID - an arbitrary (32 bit) identifier for a communication endpoint
(a service) in the network of running "immortals". This is typically
generated automatically the first time each application process starts.
It is distinct from the destination *name*.
* "Fire and Forget" RPC - launch a remote computation, but provide no information back to the caller. Note that even an async/await
RPC with "void" return value exposes more than this, because the caller can ascertain when the remote RPC has been completely processed.
* Destination name - the string identifying a communication endpoint, often
human readable.
* Sequence ID - the (monotonically increasing) number of a log entry. Note that
each logical immortal has its own log.
* "Async/await" RPCs - are *futures*; they return a value back to the
caller. Because AMBROSIA ensures reliability, they are semantically
identical to function calls, without introducing new failure modes such as
timeouts or disconnections.
* "Fire and Forget" RPCs - launch a remote computation, but provide no
information back to the caller. Note that even an async/await RPC with
"void" return value indicates more to the caller (namely, that the remote
computation has completed).
Required Helper Functions
-------------------------
FINISHME:
In order to build the binary message formats described below, we assume that the
new client software can access TCP sockets and additionally implements the
following serialized datatypes.
Assumes TCP +
* ZigZagInt - a zig-zag encoded 32-bit signed integer
* ZigZagLong - a zig-zag encoded 64-bit signed integer
* IntFixed - a 32-bit little endian number
* LongFixed - a 64-bit little endian number
* WriteZigZagInt
* WriteFixedInt
* WriteZigZagLong
* WriteFixedLong
* CheckSum - FINISHME
The variable-length integers are in the same format used by, e.g.,
[Protobufs](https://developers.google.com/protocol-buffers/docs/encoding).
* CheckSum
Message Formats
---------------
@ -43,7 +68,7 @@ Message Formats
All information received from the reliability coordinator is in the form of a sequence of log records.
Each log record has a 24 byte header, followed by the actual record contents. The header is as follows:
* Bytes [0-3]: The commit ID for the service, this should be constant for all records for the lifetime of the service, format IntFixed.
* Bytes [0-3]: The committer ID for the service, this should be constant for all records for the lifetime of the service, format IntFixed.
* Bytes [4-7]: The size of the whole log record, in bytes, including the header. The format is IntFixed
* Bytes [8-15]: The check bytes to check the integrity of the log record. The format is LongFixed.
* Bytes [16-23]: The log record sequence ID. Excluding records labeled with sequence ID “-1”, these should be in order. The format is LongFixed
@ -58,8 +83,8 @@ The rest of the record is a sequence of messages, packed tightly, each with the
All information sent to the reliability coordinator is in the form of a sequence of messages with the format specified above.
Message types and associated data which may be sent to or received by services:
* 14 - TrimTo (RRN: INTERNAL!??!)
* 13 - CountReplayableRPCBatchByte (RRN: INTERNAL!??!)
* 14 - TrimTo (FINISHME - INTERNAL!??!)
* 13 - CountReplayableRPCBatchByte (FINISHME - INTERNAL!??!)
* 12 – `UpgradeService` (Received): No data
@ -69,7 +94,12 @@ Message types and associated data which may be sent to or received by services:
* 9 – `InitialMessage` (Sent/Received): Data is a complete (incoming rpc) message which is given back to the service as the very first RPC message it ever receives. Used to bootstrap service start behavior.
* 8 – `Checkpoint` (Sent/Received): Data are the bytes corresponding to the serialized state of the service.
* 8 – `Checkpoint` (Sent/Received): The payload is a single 64 bit number.
That payload in turn is the size in bytes of a checkpoint itself, which is a
binary blob that follows this message immediately (no additional header).
The reason that checkpoints are not sent in the message payload directly is
so that they can have a 64-bit instead of 32-bit length, in order to support
large checkpoints.
* 5 – `RPCBatch` (Sent/Received): Data are a count of the number of RPC messages in the batch, followed by the corresponding number of RPC messages. Note that the count is in variable sized WriteInt format
@ -135,6 +165,7 @@ If performing an upgrade what-if test:
When a TakeCheckpoint message is received, no further messages may be processed until the state is serialized and sent in a checkpoint message. Note that the serialized state must include any unsent output messages which resulted from previous incoming calls. Those serialized unsent messages must follow the checkpoint message.
(RRN: What are the rules for SENDING!!)
### Attach-before-send protocol
FINISHME...
(RRN: What is the ATTACH protocol??)

Просмотреть файл

@ -1,9 +1,12 @@
# Put your -D variables here, e.g. -DDEBUG
DEFINES= -DIPV4
# Put your -D variables here:
DEFINES ?=
EXTRA_DEFINES = -DIPV4
# ^ TODO build everything twice for IPV6 vs IPV4.
# TODOTODO fix it so that one compile can work for both.
ALL_DEFINES = $(DEFINES) $(EXTRA_DEFINES)
GNULIBS= -lpthread
GNUOPTS= -pthread -O3
@ -16,13 +19,16 @@ OBJS1= $(patsubst src/%.c,bin/static/%.o, $(SRCS) )
OBJS2= $(patsubst src/%.c,bin/shared/%.o, $(SRCS) )
COMP= gcc $(DEFINES) -I include/ $(GNUOPTS)
COMP= gcc $(ALL_DEFINES) -I include/ $(GNUOPTS)
LINK= gcc
LIBNAME=libambrosia
all: bin/$(LIBNAME).a bin/$(LIBNAME).so
debug:
$(MAKE) DEFINES="-DAMBCLIENT_DEBUG" clean publish
bin/$(LIBNAME).a: $(OBJS1)
ar rcs $@ $(OBJS1)

Просмотреть файл

@ -53,8 +53,6 @@ enum MsgType { RPC=0, //
// FIXME: these should become PRIVATE to the library:
extern int g_to_immortal_coord, g_from_immortal_coord;
extern int upport, downport;
// Communicates with the server to establish normal operation.
//
@ -62,9 +60,14 @@ extern int upport, downport;
// received from a call to connect_sockets.
void startup_protocol(int upfd, int downfd);
// Connect the
void connect_sockets(int* upptr, int* downptr);
// Connect to the ImmortalCoordinator. Use the provided ports.
//
// On the "up" port we connect, and on "down" the coordinator connects
// to us. This function writes the file descriptors for the opened
// connections into the pointers provided as the last two arguments.
void connect_sockets(int upport, int downport, int* up_fd_ptr, int* down_fd_ptr);
// Encoding and Decoding message types
//------------------------------------------------------------------------------
// PRECONDITION: sufficient space free at output pointer.
@ -94,6 +97,8 @@ void amb_send_outgoing_rpc(void* tempbuf, char* dest, int32_t destLen, char RPC_
void amb_recv_log_hdr(int sockfd, struct log_hdr* hdr);
//------------------------------------------------------------------------------
// TEMP - audit me
void attach_if_needed(char* dest, int destLen);
@ -125,8 +130,18 @@ int zigzag_int_size(int32_t value);
//------------------------------------------------------------------------------
#ifdef AMBCLIENT_DEBUG
extern volatile int64_t amb_debug_lock;
extern volatile int64_t debug_lock;
// Helper used only below
static inline void amb_sleep_seconds(double n) {
#ifdef _WIN32
Sleep((int)(n * 1000));
#else
int64_t nanos = (int64_t)(10e9 * n);
const struct timespec ts = {0, nanos};
nanosleep(&ts, NULL);
#endif
}
static inline void amb_debug_log(const char *format, ...)
{
@ -134,14 +149,14 @@ static inline void amb_debug_log(const char *format, ...)
va_start(args, format);
amb_sleep_seconds((double)(rand()%1000) * 0.00001); // .01 - 10 ms
#ifdef _WIN32
while ( 1 == InterlockedCompareExchange64(&debug_lock, 1, 0) ) { }
while ( 1 == InterlockedCompareExchange64(&amb_debug_lock, 1, 0) ) { }
#else
while ( 1 == __sync_val_compare_and_swap(&debug_lock, 1, 0) ) { }
while ( 1 == __sync_val_compare_and_swap(&amb_debug_lock, 1, 0) ) { }
#endif
fprintf(amb_dbg_fd," [AMBCLIENT] ");
vfprintf(amb_dbg_fd,format, args);
fflush(amb_dbg_fd);
debug_lock = 0;
amb_debug_lock = 0;
va_end(args);
}
#else

Просмотреть файл

@ -2,7 +2,8 @@
// Internal helper: try repeatedly on a socket until all bytes are sent.
static inline void socket_send_all(int sock, const void* buf, size_t len, int flags) {
static inline
void socket_send_all(int sock, const void* buf, size_t len, int flags) {
char* cur = (char*)buf;
int remaining = len;
while (remaining > 0) {
@ -21,3 +22,18 @@ static inline void socket_send_all(int sock, const void* buf, size_t len, int fl
#endif
}
}
static inline
void print_hex_bytes(FILE* fd, char* ptr, int len) {
const int limit = 100; // Only print this many:
fprintf(fd,"0x");
int j;
for (j=0; j < len && j < limit; j++) {
fprintf(fd,"%02hhx", (unsigned char)ptr[j]);
if (j % 2 == 1)
fprintf(fd," ");
}
if (j<len) fprintf(fd,"...");
}

Просмотреть файл

@ -25,18 +25,12 @@
#include "ambrosia/client.h"
#include "ambrosia/internal/bits.h"
// Library-level global variables:
// Library-level (private) global variables:
// --------------------------------------------------
// FIXME: looks like we need a hashtable after all...
int g_attached = 0; // For now, ONE destination.
// This follows the rule that the RECV side acts as the server:
int upport = 1000; // Send. Up to the reliability-coordinator-as-server
int downport = 1001; // Recv. Down from the coordinator (we're server)
// Global variables that should be initialized once for the library.
// We can ONLY ever have ONE reliability coordinator.
int g_to_immortal_coord, g_from_immortal_coord;
@ -50,7 +44,7 @@ const char* coordinator_host = "::1";
#endif
#ifdef AMBCLIENT_DEBUG
// volatile int64_t debug_lock = 0;
volatile int64_t amb_debug_lock = 0;
#endif
@ -71,18 +65,6 @@ char* amb_get_error_string() {
return strerror(errno);
#endif
}
void print_hex_bytes(FILE* fd, char* ptr, int len) {
const int limit = 100; // Only print this many:
fprintf(fd,"0x");
int j;
for (j=0; j < len && j < limit; j++) {
fprintf(fd,"%02hhx", (unsigned char)ptr[j]);
if (j % 2 == 1)
fprintf(fd," ");
}
if (j<len) fprintf(fd,"...");
}
void print_decimal_bytes(char* ptr, int len) {
const int limit = 100; // Only print this many:
@ -201,27 +183,6 @@ void* amb_write_outgoing_rpc(void* buf, char* dest, int32_t destLen, char RPC_or
return (void*)cursor;
}
// This is a convenience method that sits above the buffer API:
// ------------------------------------------------------------
/*
// Logically the same as send_outgoing_*, except uses the global buffer.
// PRECONDITION: buffer is free / no outstanding "reserve" that needs to be released.
void buffer_outgoing_rpc_hdr(char* dest, int32_t destLen, char RPC_or_RetVal,
int32_t methodID, char fireForget, int argsLen) {
// Overestimate the space needed:
int sizeBound = (1 // type tag
+ 5 + destLen + 1 // RPC_or_RetVal
+ 5 + 1 // fireForget
+ 5);
char* start = reserve_buffer(sizeBound);
char* end = amb_write_outgoing_rpc_hdr(start, dest,destLen,RPC_or_RetVal,methodID,fireForget,argsLen);
// If we want to create RPCBatch messages we need to only send complete messages, not just headers:
finished_reserve_buffer(end-start);
}
*/
// Direct socket sends/recvs
// ------------------------------
@ -291,29 +252,6 @@ void attach_if_needed(char* dest, int destLen) {
}
}
/*
// INEFFICIENT version that makes an extra copy:
void send_message(char* buf, int len) {
attach_if_needed(destName, ??); // Hard-coded global dest name.
// FIXME - LAME COPY to PREPEND header bytes!
char* sendbuf = (char*)malloc(1 + 5 + destLen + 1 + 5 + 1 + len);
char* newpos = amb_write_outgoing_rpc(sendbuf, destName, destLen, 0, TPUT_MSG_ID, 1, buf, len);
// FIXME: one system call per message!
socket_send_all(g_to_immortal_coord, sendbuf, newpos-sendbuf, 0);
#ifdef AMBCLIENT_DEBUG
amb_debug_log("Sent %d byte message up to coordinator, argsLen %d...\n Hex: ", newpos-sendbuf, len);
print_hex_bytes(amb_dbg_fd, sendbuf, newpos-sendbuf);
fprintf(amb_dbg_fd,"\n Decimal: ");
print_decimal_bytes(sendbuf, newpos-sendbuf); printf("\n");
#endif
free(sendbuf);
}
*/
// Begin connect_sockets:
// --------------------------------------------------
@ -343,7 +281,7 @@ void enable_fast_loopback(SOCKET sock) {
}
}
void connect_sockets(int* upptr, int* downptr) {
void connect_sockets(int upport, int downport, int* upptr, int* downptr) {
WSADATA wsa;
SOCKET sock;
@ -502,7 +440,7 @@ void connect_sockets(int* upptr, int* downptr) {
// Establish both connections with the reliability coordinator.
// Takes two output parameters where it will write the resulting sockets.
void connect_sockets(int* upptr, int* downptr) {
void connect_sockets(int upport, int downport, int* upptr, int* downptr) {
#ifdef IPV4
struct hostent* immortalCoord;
struct sockaddr_in addr;
@ -632,6 +570,7 @@ void startup_protocol(int upfd, int downfd) {
case TakeBecomingPrimaryCheckpoint:
amb_debug_log("Starting up for the first time (TakeBecomingPrimaryCheckpoint)\n");
break;
case Checkpoint:
fprintf(stderr, "RECOVER mode ... not implemented yet.\n");

Просмотреть файл

@ -230,10 +230,6 @@ char* reserve_buffer(int len)
void release_buffer(int len)
{
// finished_reserve_buffer(len);
// g_buffer_tail += g_buffer_total_reserved; // Publish it!
// g_buffer_total_reserved = 0;
spsc_rring_debug_log(" => release_buffer of %d bytes, new tail %d\n", len, g_buffer_tail + len);
if (len > g_buffer_last_reserved) {

Просмотреть файл

@ -23,11 +23,12 @@
# Oh, that's happening even on ambrosia-dev:
FROM ambrosia-dev
# We get build dependencies from ambrosia-dev, since it built the native library.
# RUN apt-get update && \
# apt-get install -y make gcc
# nmap
RUN apt-get update && \
apt-get install -y make gcc nmap
ADD . /ambrosia/NativeService
ADD . /ambrosia/NativeService
WORKDIR /ambrosia/NativeService
ENV AMBROSIA_BINDIR=/ambrosia/bin

Просмотреть файл

@ -20,7 +20,7 @@ LIBS= -L $(AMBROSIA_BINDIR) -l:libambrosia.a -lpthread
LINK= gcc
all: service_v4.exe service_v6.exe
dbg: service_dbg_v4.exe
debug: service_dbg_v4.exe
service_temp.exe: service.c
$(COMP) service.c -o service.o
@ -29,10 +29,12 @@ service_temp.exe: service.c
service_v4.exe: $(HEADERS) $(SRCS) service.c
$(MAKE) DEFINES="-DIPV4" partclean service_temp.exe
mv service_temp.exe $@
ln -sf $@ service.exe
service_dbg_v4.exe: $(HEADERS) $(SRCS) service.c
$(MAKE) DEFINES="-DAMBCLIENT_DEBUG -DIPV4" partclean service_temp.exe
mv service_temp.exe $@
ln -sf $@ service.exe
service_v6.exe: $(HEADERS) $(SRCS) service.c
$(MAKE) DEFINES="-DIPV6" partclean service_temp.exe
@ -42,7 +44,7 @@ partclean:
rm -f service_temp.exe
clean:
rm -f service_winsockv4.exe service_winsockv6.exe service_v4.exe service_v6.exe
rm -f service_winsockv4.exe service_winsockv6.exe service_v4.exe service_v6.exe service_dbg_v4.exe serice_temp.exe
rm -f \#* .\#* *~
.PHONY: lin clean partclean

Просмотреть файл

@ -1,25 +0,0 @@
#!/bin/bash
set -xeuo pipefail
# Run both processes together in one script. This is intended to be
# used within the Docker container built by Dockerfile.
# TODO: set up your credentials before calling this script:
# export AZURE_STORAGE_CONN_STRING="..."
echo "Launching CRA worker"
# strace dotnet "/ambrosia/ImmortalCoordinator/bin/Release/netcoreapp2.0/linux-x64/ImmortalCoordinator.dll" rrnjob 1500 2> /tmp/coord.log &
dotnet "/ambrosia/ImmortalCoordinator/bin/Release/netcoreapp2.0/linux-x64/ImmortalCoordinator.dll" rrnjob 1500 &
PID1=$!
sleep 1
# dotnet "/ambrosia/ImmortalCoordinator/bin/Release/netcoreapp2.0/linux-x64/ImmortalCoordinator.dll" rrnserver 2500 &
# PID2=$!
# Lame, racy:
sleep 7
echo "Proceeding on the assumption you saw \"Ready...\" above this line..."
./service_v4.exe
wait $PID1
# wait $PID2

Просмотреть файл

@ -11,10 +11,17 @@ set -euo pipefail
# source `dirname $0`/default_var_settings.sh
PORT1=49001
PORT2=49002
PORT3=49003
PORT4=49004
# A number to add to all ports to avoid colliding or reusing recently
# used ports.
if ! [ ${PORTOFFSET:+defined} ]; then
PORTOFFSET=0
fi
PORT1=$((49001 + PORTOFFSET))
PORT2=$((49002 + PORTOFFSET))
PORT3=$((49003 + PORTOFFSET))
PORT4=$((49004 + PORTOFFSET))
INSTANCE_PREFIX=""
if [ $# -ne 0 ];
@ -23,26 +30,32 @@ fi
CLIENTNAME=${INSTANCE_PREFIX}nativeSend
SERVERNAME=${INSTANCE_PREFIX}nativeRecv
_cleanup() {
kill -TERM "$pid_server" 2>/dev/null || true
}
trap _cleanup TERM INT QUIT HUP
echo
echo "--------------------------------------------------------------------------------"
echo "Running NativeService with 4 processes all in this machine/container"
echo " Instance: names $CLIENTNAME, $SERVERNAME"
echo "--------------------------------------------------------------------------------"
echo
set -x
time Ambrosia RegisterInstance -i $CLIENTNAME --rp $PORT1 --sp $PORT2 -l "./ambrosia_logs/"
time Ambrosia RegisterInstance -i $SERVERNAME --rp $PORT3 --sp $PORT4 -l "./ambrosia_logs/"
set +x
# AMBROSIA_IMMORTALCOORDINATOR_PORT=2500 runAmbrosiaService.sh ./service_v4.exe 0 $SERVERNAME $PORT1 $PORT2 24 1 20
if ! [ ${SKIP_REGISTER:+defined} ]; then
set -x
time Ambrosia RegisterInstance -i $CLIENTNAME --rp $PORT1 --sp $PORT2 -l "./ambrosia_logs/"
time Ambrosia RegisterInstance -i $SERVERNAME --rp $PORT3 --sp $PORT4 -l "./ambrosia_logs/"
set +x
fi
echo
echo "NativeService: Launching Receiver"
set -x
AMBROSIA_INSTANCE_NAME=$SERVERNAME AMBROSIA_IMMORTALCOORDINATOR_PORT=1500 \
AMBROSIA_INSTANCE_NAME=$SERVERNAME AMBROSIA_IMMORTALCOORDINATOR_PORT=$((1600 + PORTOFFSET)) \
COORDTAG=CoordRecv AMBROSIA_IMMORTALCOORDINATOR_LOG=./recvr.log \
runAmbrosiaService.sh ./service_v4.exe 1 $CLIENTNAME $PORT3 $PORT4 24 1 20 &
runAmbrosiaService.sh ./service.exe 1 $CLIENTNAME $PORT3 $PORT4 24 1 20 &
pid_server=$!
set +x
@ -56,9 +69,9 @@ fi
echo
echo "NativeService: Launching Sender now:"
set -x
AMBROSIA_INSTANCE_NAME=$CLIENTNAME AMBROSIA_IMMORTALCOORDINATOR_PORT=2500 \
AMBROSIA_INSTANCE_NAME=$CLIENTNAME AMBROSIA_IMMORTALCOORDINATOR_PORT=$((1601 + PORTOFFSET)) \
COORDTAG=CoordSend AMBROSIA_IMMORTALCOORDINATOR_LOG=./sender.log \
runAmbrosiaService.sh ./service_v4.exe 0 $SERVERNAME $PORT1 $PORT2 24 1 20
runAmbrosiaService.sh ./service.exe 0 $SERVERNAME $PORT1 $PORT2 24 1 20
set +x
echo

Просмотреть файл

@ -37,8 +37,9 @@
// TODO: remove internal dependency:
#include "ambrosia/internal/spsc_rring.h"
#include "ambrosia/client.h"
#include "ambrosia/internal/bits.h"
// Extra utilities (print_hex_bytes, socket_send_all):
#include "ambrosia/internal/bits.h"
// Library-level global variables:
// --------------------------------------------------
@ -100,21 +101,6 @@ int destLen; // Initialized below..
// General helper functions
// --------------------------------------------------------------------------------
#ifdef AMBCLIENT_DEBUG
// DUPLICATED with ambrosia_client.
void print_hex_bytes(FILE* fd, char* ptr, int len) {
const int limit = 100; // Only print this many:
fprintf(fd,"0x");
int j;
for (j=0; j < len && j < limit; j++) {
fprintf(fd,"%02hhx", (unsigned char)ptr[j]);
if (j % 2 == 1)
fprintf(fd," ");
}
if (j<len) fprintf(fd,"...");
}
#endif
// Hacky busy-wait by thread-yielding for now:
static inline void yield_thread() {
#ifdef _WIN32
@ -587,7 +573,7 @@ void* network_progress_thread( void* lpParam )
} else if ( spin_tries == 0) {
spin_tries = hot_spin_amount;
// amb_debug_log(" network thread: yielding to wait...\n");
#ifdef AMBCLIENT_DEBUG
#ifdef AMBCLIENT_DEBUG
sleep_seconds(0.5);
sleep_seconds(0.05);
#endif
@ -625,10 +611,12 @@ void reset_trial_state() {
}
}
int main(int argc, char** argv)
{
// How big to allocate the buffer:
int buffer_bytes_allocated = -1; // Ivar semantics - write once.
int upport, downport;
srand(time(0));
@ -664,6 +652,7 @@ int main(int argc, char** argv)
destLen = strlen(destName);
upport = atoi(argv[3]);
downport = atoi(argv[4]);
} else {
fprintf(stderr, "Usage: this executable expects args: <role=0/1/2/3> <destination> <port> <port> [roundsz] [trials] [bufsz]\n");
fprintf(stderr, " where <role> is 0/1 for sender/receiver throughput mode\n");
@ -681,6 +670,11 @@ int main(int argc, char** argv)
fprintf(stderr, "\nERROR: Bytes-per-round should be bigger than max message size.\n");
abort();
}
if ( g_is_sender || destLen == 0)
printf("We are running the SENDER\n");
else
printf("We are running the RECEIVER\n");
printf("Connecting to my coordinator on ports: %d (up), %d (down)\n", upport, downport);
printf("The 'up' port we connect, and the 'down' one the coordinator connects to us.\n");
@ -690,7 +684,7 @@ int main(int argc, char** argv)
/* printf("(You need four ports, in the above example: 50000-50003 .)\n"); */
int upfd, downfd;
connect_sockets(&upfd, &downfd);
connect_sockets(upport, downport, &upfd, &downfd);
amb_debug_log("Connections established (%d,%d), beginning protocol.\n", upfd, downfd);
startup_protocol(upfd, downfd);

Просмотреть файл

@ -1,7 +1,7 @@
# Perform the code-generation step for this example application.
if (-not ( $env:AMBVARIANT )) {
$env:AMBVARIANT="x64\Debug\netcoreapp2.0"
if (-not ( $env:AMBVARIANTCORE )) {
$env:AMBVARIANTCORE="x64\Debug\netcoreapp2.0"
}
# Build the API projects
@ -17,9 +17,9 @@ Copy-Item "IJob\bin\Debug\netcoreapp2.0\publish\*" -Force -Destination "CodeGenD
Copy-Item "..\..\Clients\CSharp\AmbrosiaCS\AmbrosiaCS.csproj" -Force -Destination "CodeGenDependencies\netcoreapp2.0\"
Write-Host "Using variant of AmbrosiaCS.exe: $env:AMBVARIANT"
Write-Host "Using variant of AmbrosiaCS.exe: $env:AMBVARIANTCORE"
Write-Host "Executing codegen command: dotnet ..\..\Clients\CSharp\AmbrosiaCS\bin\$env:AMBVARIANT\AmbrosiaCS.dll CodeGen -a=API\bin\$env:AMBVARIANT\ServerAPI.dll -a=IJob\bin\$env:AMBVARIANT\IJob.dll -o=PTIAmbrosiaGeneratedAPINetCore -f=netcoreapp2.0 -b=CodeGenDependencies\netcoreapp2.0"
Write-Host "Executing codegen command: dotnet ..\..\Clients\CSharp\AmbrosiaCS\bin\$env:AMBVARIANTCORE\AmbrosiaCS.dll CodeGen -a=API\bin\$env:AMBVARIANTCORE\ServerAPI.dll -a=IJob\bin\$env:AMBVARIANTCORE\IJob.dll -o=PTIAmbrosiaGeneratedAPINetCore -f=netcoreapp2.0 -b=CodeGenDependencies\netcoreapp2.0"
# Generate the assemblies, assumes an .exe which is created by a .Net Framework build:
& dotnet "..\..\Clients\CSharp\AmbrosiaCS\bin\$env:AMBVARIANT\AmbrosiaCS.dll" CodeGen -a="API\bin\$env:AMBVARIANT\ServerAPI.dll" -a="IJob\bin\$env:AMBVARIANT\IJob.dll" -o="PTIAmbrosiaGeneratedAPINetCore" -f="netcoreapp2.0" -b="CodeGenDependencies\netcoreapp2.0"
& dotnet "..\..\Clients\CSharp\AmbrosiaCS\bin\$env:AMBVARIANTCORE\AmbrosiaCS.dll" CodeGen -a="API\bin\$env:AMBVARIANTCORE\ServerAPI.dll" -a="IJob\bin\$env:AMBVARIANTCORE\IJob.dll" -o="PTIAmbrosiaGeneratedAPINetCore" -f="netcoreapp2.0" -b="CodeGenDependencies\netcoreapp2.0"

Просмотреть файл

@ -4,8 +4,6 @@ PORT2=50002
PORT3=50003
PORT4=50004
CLIENTNAME=dockertest1
SERVERNAME=dockertest2
CRAPORT1=1500
CRAPORT2=50500

Просмотреть файл

@ -18,6 +18,19 @@ set -euo pipefail
cd `dirname $0`
source ./default_var_settings.sh
# PORTOFFSET: A number to add to all ports to avoid colliding or
# reusing recently used ports.
if ! [ ${PORTOFFSET:+defined} ]; then
PORTOFFSET=0
else
PORT1=$((PORT1 + PORTOFFSET))
PORT2=$((PORT2 + PORTOFFSET))
PORT3=$((PORT3 + PORTOFFSET))
PORT4=$((PORT4 + PORTOFFSET))
CRAPORT1=$((CRAPORT1 + PORTOFFSET))
CRAPORT2=$((CRAPORT2 + PORTOFFSET))
fi
INSTANCE_PREFIX=""
if [ $# -ne 0 ];
then INSTANCE_PREFIX="$1"
@ -38,16 +51,22 @@ echo " Instance: names $CLIENTNAME, $SERVERNAME"
echo "--------------------------------------------------------------------------------"
echo
set -x
time Ambrosia RegisterInstance -i $CLIENTNAME --rp $PORT1 --sp $PORT2 -l "./ambrosia_logs/"
time Ambrosia RegisterInstance -i $SERVERNAME --rp $PORT3 --sp $PORT4 -l "./ambrosia_logs/"
set +x
if ! [ ${SKIP_REGISTER:+defined} ]; then
set -x
time Ambrosia RegisterInstance -i $CLIENTNAME --rp $PORT1 --sp $PORT2 -l "./ambrosia_logs/"
time Ambrosia RegisterInstance -i $SERVERNAME --rp $PORT3 --sp $PORT4 -l "./ambrosia_logs/"
set +x
fi
which runAmbrosiaService.sh
slog=`mktemp server.XXXX.log`
jlog=`mktemp job.XXXX.log`
echo
echo "PTI: Launching Server:"
set -x
AMBROSIA_INSTANCE_NAME=$SERVERNAME AMBROSIA_IMMORTALCOORDINATOR_PORT=$CRAPORT1 \
COORDTAG=CoordServ AMBROSIA_IMMORTALCOORDINATOR_LOG=./server.log \
COORDTAG=CoordServ AMBROSIA_IMMORTALCOORDINATOR_LOG=$slog \
runAmbrosiaService.sh ./Server/publish/Server --rp $PORT4 --sp $PORT3 -j $CLIENTNAME -s $SERVERNAME -n 1 -c &
set +x
pid_server=$!
@ -64,7 +83,7 @@ echo
echo "PTI: Launching Job now:"
set -x
AMBROSIA_INSTANCE_NAME=$CLIENTNAME AMBROSIA_IMMORTALCOORDINATOR_PORT=$CRAPORT2 \
COORDTAG=CoordCli AMBROSIA_IMMORTALCOORDINATOR_LOG=./job.log \
COORDTAG=CoordCli AMBROSIA_IMMORTALCOORDINATOR_LOG=$jlog \
runAmbrosiaService.sh ./Client/publish/Job --rp $PORT2 --sp $PORT1 -j $CLIENTNAME -s $SERVERNAME --mms 65536 -n 2 -c
set +x
@ -78,5 +97,6 @@ echo "Attempt a cleanup of our table metadata:"
set -x
UnsafeDeregisterInstance $CLIENTNAME || true
UnsafeDeregisterInstance $SERVERNAME || true
rm $slog $jlog
set +x
echo "All done."

111
README.md
Просмотреть файл

@ -14,7 +14,7 @@ dramatically lowers development and deployment costs and time to
market by automatically providing recovery and high availability.
Today's datacenter oriented applications, which include most popular
services running in Azure today, are composed of highly complex,
services running in the cloud today, are composed of highly complex,
distributed software stacks. For instance, they typically incorporate
Event Hub or Kafka to robustly journal input and interactions for
recoverability, log important information to stores like Azure blobs
@ -26,8 +26,51 @@ service code.
In contrast, Ambrosia automatically gives programmers recoverability,
high availability, debuggability, upgradability, and exactly once
execution, without requiring developers to weave together such complex
systems, or use overly expensive mechanisms. Check out the overview
deck linked to the left to learn more or email us.
systems, or use overly expensive mechanisms.
To learn more about Ambrosia's implementation and performance you can read our [whitepaper](https://www.microsoft.com/en-us/research/publication/a-m-b-r-o-s-i-a-providing-performant-virtual-resiliency-for-distributed-applications/).
Table of Contents
-----------
* [AMBROSIA Concepts](#ambrosia-concepts)
* [How it works](#how-it-works)
* [Features](#features)
* [Getting started]()
* [Windows]()
* [Kubernetes]()
* [Reference](#reference)
* [Language Support](#language-support)
* [Usage](#usage)
## AMBROSIA Concepts
### Virtual Resiliency
> *Virtual Resiliency* is a mechanism in a (possibly distributed) programming and execution environment, typically employing a log, which exploits the replayably deterministic nature and serializability of an application to automatically mask failure.
We use the term virtual resiliency to describe the mechanism in AMBROSIA that allows programmers to write their applications in a failure oblivious way, removing the need for application writers to write logic for recovery or state protection. Data processing systems, which typically express their queries in SQL variants, have provided their query writers virtual resiliency for decades. Map-reduce systems, which dont necessarily use SQL, also provide this capability. Note that in all these cases, this feature leverages the ability to deterministically replay, like AMBROSIA.
### Deterministic Replayability
In order to achieve virtual resiliency through AMBROSIA, applications but uphold the following contract: from some initial state, any execution of the same requests in the same order results in both the *same final state*, as well as the *same outgoing requests in the same order*.
### Immortals
The basic building blocks of AMBROSIA are *Immortals*, reliable distributed objects that communicate through RPCs. An Immortal defines a set of persistent state and a set of RPC handlers that operate on that state. An *instance* of an Immortal is a named entity that maintains state and executes RPC handlers according to the Immortal's definition. An AMBROSIA application often has multiple instances of the same Immortal; for example, an application may define a single "job" Immortal for running a data-processing job and run multiple instances of that job operating on different data sets.
## How it works
The figure below outlines the basic architecture of an AMBROSIA application, showing two communicating AMBROSIA services, called Immortals. Each inner box in the figure represents a separate process running as part of the Immortal. Each instance of an Immortal exists as a software object and thread of control running inside of an application process. An Immortal instance communicates with other Immortal instances through an *Immortal Coordinator* process, which durably logs the instance's RPCs and encapsulates the low-level networking required to send RPCs. The position of requests in the log determines the order in which they are submitted to the application process for execution and then re-execution upon recovery.
![Ambrosia Architecture](Architecture.svg)
In addition, the language specific AMBROSIA binding provides a state serializer. To avoid replaying from the start of the service during recovery, the Immortal Coordinator occasionally checkpoints the state of the Immortal, which includes the application state. The way this serialization is provided can vary from language to language, or even amongst bindings for the same language.
## Features
Here is a list of features that AMBROSIA provides to application developers and deployers:
* Register Instance, Add Replica
* Debug Instance
* Active Active
* Live Upgrades, Test Upgrades
* RPC
* Asynchronous RPC (beta)
Quick Start: Fetch a binary distribution
----------------------------------------
@ -51,3 +94,65 @@ Running a Sample
----------------
FINISHME - AmbrosiaDocs.md content will move here!!
## Reference
### Language Support
AMBROSIA currently supports C# on both .NET Core and .NET Framework. We plan to exand this support with AMBROSIA bindings for other languages in the future.
### Usage
```
Usage: Ambrosia.exe RegisterInstance [OPTIONS]
Options:
-i, --instanceName=VALUE The instance name [REQUIRED].
--rp, --receivePort=VALUE
The service receive from port [REQUIRED].
--sp, --sendPort=VALUE The service send to port. [REQUIRED]
-l, --log=VALUE The service log path.
--cs, --createService=VALUE
[A - AutoRecovery | N - NoRecovery | Y -
AlwaysRecover].
--ps, --pauseAtStart Is pause at start enabled.
--npl, --noPersistLogs Is persistent logging disabled.
--lts, --logTriggerSize=VALUE
Log trigger size (in MBs).
--aa, --activeActive Is active-active enabled.
--cv, --currentVersion=VALUE
The current version #.
--uv, --upgradeVersion=VALUE
The upgrade version #.
-h, --help show this message and exit
Usage: Ambrosia.exe AddReplica [OPTIONS]
Options:
-r, --replicaNum=VALUE The replica # [REQUIRED].
-i, --instanceName=VALUE The instance name [REQUIRED].
--rp, --receivePort=VALUE
The service receive from port [REQUIRED].
--sp, --sendPort=VALUE The service send to port. [REQUIRED]
-l, --log=VALUE The service log path.
--cs, --createService=VALUE
[A - AutoRecovery | N - NoRecovery | Y -
AlwaysRecover].
--ps, --pauseAtStart Is pause at start enabled.
--npl, --noPersistLogs Is persistent logging disabled.
--lts, --logTriggerSize=VALUE
Log trigger size (in MBs).
--aa, --activeActive Is active-active enabled.
--cv, --currentVersion=VALUE
The current version #.
--uv, --upgradeVersion=VALUE
The upgrade version #.
-h, --help show this message and exit
Usage: Ambrosia.exe DebugInstance [OPTIONS]
Options:
-i, --instanceName=VALUE The instance name [REQUIRED].
--rp, --receivePort=VALUE
The service receive from port [REQUIRED].
--sp, --sendPort=VALUE The service send to port. [REQUIRED]
-l, --log=VALUE The service log path.
-c, --checkpoint=VALUE The checkpoint # to load.
--cv, --currentVersion=VALUE
The version # to debug.
--tu, --testingUpgrade Is testing upgrade.
-h, --help show this message and exit
```

Просмотреть файл

@ -18,6 +18,33 @@ else
exit 1
fi
# Helpers
# ------------------------------------------------------------
# Compute the relative path to A starting from directory B
# Output the resulting relative path to stdout.
if which realpath 2>&1 >/dev/null ; then
function getrelative() {
realpath "$1" --relative-to="$2"
}
elif which python 2>&1 >/dev/null ; then
function getrelative() {
python -c "import os,sys;print(os.path.relpath(*(sys.argv[1:])))" "$1" "$2"
}
# elif which perl; then
else
echo "ERROR $0: can't find an easy way to compute relative paths on this system."
exit 1
fi
# ------------------------------------------------------------
echo "Begin script in mode = $mode"
cd `dirname $0`/../bin
@ -57,11 +84,9 @@ for dir in $secondary; do
symlink)
while read f; do
echo -ne "."
# echo "ln -sf $relative $f"
# Requires realpath from GNU coreutils:
dirof=`dirname $f`
relative=`realpath ../runtime/$f --relative-to=$dirof`
ln -sf $relative $f
relativepath=$(getrelative "../runtime/$f" "$dirof")
ln -sf $relativepath $f
done < $dups
echo
;;
@ -109,11 +134,12 @@ else
cd "$bindir/$dir"
mv -n * ..
done
echo
echo "After squishing, these files left behind / conflicting:"
echo "-------------------------------------------------------"
cd "$bindir"
find $secondary
echo "-------------------------------------------------------"
echo "Every one of these represents a risk of undefined behavior!"
echo
fi

Просмотреть файл

@ -79,6 +79,33 @@ fi
# Helper functions
# --------------------------------------------------
# Global variable set below:
tail_pid=""
coord_pid=""
app_pid=""
keep_monitoring=""
_normal_cleanup() {
kill -TERM "$coord_pid" 2>/dev/null || true
kill -TERM "$tail_pid" 2>/dev/null || true
kill -TERM "$app_pid" 2>/dev/null || true
rm -f "$keep_monitoring" 2>/dev/null || true
}
_unexpected_cleanup() {
trap '' EXIT # some shells will call EXIT after the INT handler
if [ ${$1:+defined} ];
then local WHCH="($1)"
else local WHCH=""
fi
echo "$0: Exiting script abnormally! Cleaning up. $WHCH"
_normal_cleanup
echo "$0: Done with cleanup."
}
trap _normal_cleanup EXIT
trap _unexpected_cleanup TERM INT QUIT
function tag_stdin() {
local MSG=$1
# A complicated but full-proof bash method of line-by-line reading:
@ -87,13 +114,10 @@ function tag_stdin() {
done
}
# Global variable set below:
tail_pid=""
function tail_tagged() {
local MSG=$1
local FILE=$2
(tail -F "$FILE" | tag_stdin $MSG) &
tail -F "$FILE" | tag_stdin $MSG &
tail_pid=$!
}
@ -125,8 +149,10 @@ function start_immortal_coordinator() {
touch "$COORDLOG"
fi
echo " $TAG Redirecting output to: $COORDLOG"
if which rotatelogs; then
# OPTION (1): Bound logs, but complicated.
# ----------------------------------------
if which rotatelogs >/dev/null ; then
# Bound the total amount of output used by the ImmortalCoordinator log:
ImmortalCoordinator $* 2>&1 | rotatelogs -f -t "$COORDLOG" 10M &
coord_pid=$!
@ -135,10 +161,14 @@ function start_immortal_coordinator() {
ImmortalCoordinator $* >>"$COORDLOG" 2>&1 &
coord_pid=$!
fi
if ! [[ ${AMBROSIA_SILENT_COORDINATOR:+defined} ]]; then
tail_tagged "$COORDTAG" "$COORDLOG"
fi
# ----------------------------------------
# OPTION (2) Don't bound coordinator log on disk. Keep it simple:
# ImmortalCoordinator $* 2>&1 | tee "$COORDLOG" &
# coord_pid=$!
while ! grep -q "Ready" "$COORDLOG" && kill -0 $coord_pid 2>/dev/null ;
do sleep 2; done
@ -154,6 +184,9 @@ function start_immortal_coordinator() {
echo " $TAG Coordinator looks ready."
}
# Script main body:
# --------------------------------------------------------------------------------
# Health monitoring. This is an example of AMBROSIA required practice:
# the coordinator and application are bound together, if one dies the
# other must (as though they were one process.)

Просмотреть файл

@ -17,6 +17,14 @@ fi
# Set up common definitions.
source Scripts/ci_common_defs.sh
function check_az_storage_and_bail() {
echo
echo "All builds completed. Proceeding to running system tests."
if ! [[ ${AZURE_STORAGE_CONN_STRING:+defined} ]]; then
echo "AZURE_STORAGE_CONN_STRING not defined, so not attempting runnning system tests."
exit 0
fi
}
case $mode in
docker)
@ -25,10 +33,19 @@ case $mode in
# When we are trying to run tests we don't really want the tarball:
DONT_BUILD_TARBALL=1 ./build_docker_images.sh
check_az_storage_and_bail
# APPLICATION 1: PTI
./Scripts/internal/run_linux_PTI_docker.sh
# ----------------------------------------
./Scripts/internal/run_linux_PTI_docker.sh
# Application 2: NativeService
# ----------------------------------------
# docker --env AZURE_STORAGE_CONN_STRING="${AZURE_STORAGE_CONN_STRING}" --rm \
# ambrosia-nativeapp ./run_test_in_one_machine.sh
;;
nodocker)
echo "Executing raw-Linux, non-Docker build."
@ -39,19 +56,13 @@ case $mode in
# ----------------------------------------
cd "$AMBROSIA_ROOT"/InternalImmortals/PerformanceTestInterruptible
./build_dotnetcore.sh
if [[ ${AZURE_STORAGE_CONN_STRING:+defined} ]]; then
echo
echo "All builds completed. Attempt to run a test."
./run_small_PTI_and_shutdown.sh $INSTPREF
else
echo "AZURE_STORAGE_CONN_STRING not defined, so not attempting PTI test."
fi
check_az_storage_and_bail
./run_small_PTI_and_shutdown.sh $INSTPREF
# Application 2: ...
# ----------------------------------------
cd "$AMBROSIA_ROOT"
;;
*)

Просмотреть файл

@ -40,17 +40,20 @@ echo
$DOCKER build -t ${TAG1A} .
if ! [[ ${DONT_BUILD_RELEASE_IMAGE:+defined} ]]; then
echo;echo "Building Release Image: $TAG1B"; echo
$DOCKER build -f Dockerfile.release -t ${TAG1B} .
fi
if ! [[ ${DONT_BUILD_APP_IMAGES:+defined} ]]; then
if ! [[ ${DONT_BUILD_PTI_IMAGE:+defined} ]]; then
if ! [[ ${DONT_BUILD_PTI_IMAGE:+defined} ]]; then
echo;echo "Building App Image: $TAG2"; echo
cd "$AMBROSIA_ROOT"/InternalImmortals/PerformanceTestInterruptible
$DOCKER build -t ${TAG2} .
cd "$AMBROSIA_ROOT"
fi
if ! [[ ${DONT_BUILD_NATIVE_IMAGE:+defined} ]]; then
if ! [[ ${DONT_BUILD_NATIVE_IMAGE:+defined} ]]; then
echo;echo "Building App Image: $TAG3"; echo
cd "$AMBROSIA_ROOT"/InternalImmortals/NativeService
docker build -t ${TAG3} .
cd "$AMBROSIA_ROOT"

Просмотреть файл

@ -126,8 +126,7 @@ echo "--------------------------------------------------------------"
if [ ${OS:+defined} ] && [ "$OS" == "Windows_NT" ];
then ./Scripts/dedup_bindist.sh squish
elif [ "$UNAME" == Darwin ];
then ./Scripts/dedup_bindist.sh squish
# FIXME ^ should symlink, but "realpath" is needed.
then ./Scripts/dedup_bindist.sh symlink
else ./Scripts/dedup_bindist.sh symlink
fi

19
docs/SecuringComms.md Normal file
Просмотреть файл

@ -0,0 +1,19 @@
# Securing Ambrosia Communications
This document describes how deployers of the Immortal Coordinator may secure all communications between coordinators. Immortal
Coordinators communicate with each using a reliable network connection library called [CRA](https://github.com/Microsoft/CRA).
CRA uses standard unsecured TCP connections by default. If the deployer wants to secure all communications between Immortal
Coordinators, they can do so by writing a _communication wrapper_ and providing this information to the coordinator. The deployer
need to provide two pieces of information related to securing connections: an assemby name and an assembly class name. Together,
this information points to an assembly that implements an interface that is called whenever a TCP connection is created between two
Immortal Coordinators. The interface is called `ISecureStreamConnectionDescriptor`, and is shown [here](https://github.com/Microsoft/CRA/blob/master/src/CRA.ClientLibrary/Security/ISecureStreamConnectionDescriptor.cs).
For example, suppose we wish to use a dummy security wrapper that simply passes the stream without securing it. See
[here](https://github.com/Microsoft/CRA/blob/master/src/CRA.ClientLibrary/Security/DummySecureStreamConnectionDescriptor.cs)
for such an example. We would provide the information to Amborosia when invoking the Immortal Coordinator, as follows.
dotnet ImmortalCoordinator.dll --instanceName=client1 --port=1500 -an=CRA.ClientLibrary -ac=CRA.ClientLibrary.DummySecureStreamConnectionDescriptor
dotnet ImmortalCoordinator.dll --instanceName=server1 --port=2500 -an=CRA.ClientLibrary -ac=CRA.ClientLibrary.DummySecureStreamConnectionDescriptor
Another example of a security wrapper is provided [here](https://github.com/Microsoft/CRA/blob/master/src/CRA.ClientLibrary/Security/SampleSecureStreamConnectionDescriptor.cs).
This example uses an X509 Certificate to create SslStream wrappers around the TCP connections.