Foundations for causal tracing

This commit is contained in:
Tony Garnock-Jones 2022-01-19 14:40:50 +01:00
parent f7a5edff39
commit 4dc613a091
25 changed files with 871 additions and 430 deletions

28
Cargo.lock generated
View File

@ -1098,9 +1098,9 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "preserves"
version = "2.2.0"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c604be1e4ddac999d7c3d81ca9f7912a33a1234f5394fb902315f8cee25645cd"
checksum = "87e17ecd8e57af358d013c7f9cc30078a86bbba63eac64e73e1c9b9651304eb8"
dependencies = [
"base64",
"dtoa",
@ -1112,9 +1112,9 @@ dependencies = [
[[package]]
name = "preserves-schema"
version = "2.3.1"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1ae4749701948512784501bcc48bfee33e8511714fac9c3acfe7fabb1d7fbdf"
checksum = "758fe4013a89071fafdc71ba79bd919f68ec9baadaa77db78b4a41c21cf8c606"
dependencies = [
"convert_case",
"glob",
@ -1318,9 +1318,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "security-framework"
version = "2.4.2"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87"
checksum = "d09d3c15d814eda1d6a836f2f2b56a6abc1446c8a34351cb3180d3db92ffe4ce"
dependencies = [
"bitflags",
"core-foundation",
@ -1331,9 +1331,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
version = "2.4.2"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e"
checksum = "e90dd10c41c6bfc633da6e0c659bd25d31e0791e5974ac42970267d59eba87f7"
dependencies = [
"core-foundation-sys",
"libc",
@ -1386,9 +1386,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.74"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142"
checksum = "c059c05b48c5c0067d4b4b2b4f0732dd65feb52daf7e0ea09cd87e7dadc1af79"
dependencies = [
"itoa 1.0.1",
"ryu",
@ -1459,9 +1459,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.25"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c"
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
dependencies = [
"clap",
"lazy_static",
@ -1890,9 +1890,9 @@ dependencies = [
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
version = "0.10.3+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
checksum = "46a2e384a3f170b0c7543787a91411175b71afd56ba4d3a0ae5678d4e2243c0e"
[[package]]
name = "wasm-bindgen"

View File

@ -8,11 +8,11 @@ use syndicate::value::NestedValue;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
syndicate::convenient_logging()?;
Actor::new(None).boot(tracing::Span::current(), |t| {
Actor::top(None, |t| {
let ds = Cap::new(&t.create(Dataspace::new(None)));
let _ = t.prevent_inert_check();
t.spawn(syndicate::name!("box"), enclose!((ds) move |t| {
t.spawn(Some(AnyValue::symbol("box")), enclose!((ds) move |t| {
let current_value = t.named_field("current_value", 0u64);
t.dataflow({
@ -49,7 +49,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}));
t.spawn(syndicate::name!("client"), enclose!((ds) move |t| {
t.spawn(Some(AnyValue::symbol("client")), enclose!((ds) move |t| {
let box_state_handler = syndicate::entity(0u32)
.on_asserted(enclose!((ds) move |count, t, captures: AnyValue| {
*count = *count + 1;

View File

@ -26,7 +26,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::from_args();
let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?;
let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split();
Actor::new(None).boot(syndicate::name!("consumer"), |t| {
Actor::top(None, |t| {
relay::connect_stream(t, i, o, false, sturdyref, (), |_state, t, ds| {
let consumer = syndicate::entity(0)
.on_message(|message_count, _t, m: AnyValue| {
@ -44,13 +44,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
observer: Arc::clone(&consumer),
});
t.linked_task(syndicate::name!("tick"), async move {
t.linked_task(Some(AnyValue::symbol("tick")), async move {
let mut stats_timer = interval(Duration::from_secs(1));
loop {
stats_timer.tick().await;
let consumer = Arc::clone(&consumer);
external_event(&Arc::clone(&consumer.underlying.mailbox),
&Account::new(syndicate::name!("account")),
None,
&Account::new(None, None),
Box::new(move |t| t.with_entity(
&consumer.underlying,
|t, e| e.message(t, AnyValue::new(true)))))?;

View File

@ -93,7 +93,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::from_args();
let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?;
let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split();
Actor::new(None).boot(syndicate::name!("pingpong"), |t| {
Actor::top(None, |t| {
relay::connect_stream(t, i, o, false, sturdyref, (), move |_state, t, ds| {
let (send_label, recv_label, report_latency_every, should_echo, bytes_padding) =
@ -172,13 +172,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
observer: Arc::clone(&consumer),
});
t.linked_task(syndicate::name!("tick"), async move {
t.linked_task(Some(AnyValue::symbol("tick")), async move {
let mut stats_timer = interval(Duration::from_secs(1));
loop {
stats_timer.tick().await;
let consumer = Arc::clone(&consumer);
external_event(&Arc::clone(&consumer.underlying.mailbox),
&Account::new(syndicate::name!("account")),
None,
&Account::new(None, None),
Box::new(move |t| t.with_entity(
&consumer.underlying,
|t, e| e.message(t, AnyValue::new(true)))))?;
@ -189,7 +190,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let turn_count = c.turn_count;
let action_count = c.action_count;
let account = Arc::clone(t.account());
t.linked_task(syndicate::name!("boot-ping"), async move {
t.linked_task(Some(AnyValue::symbol("boot-ping")), async move {
let padding = AnyValue::bytestring(vec![0; bytes_padding]);
for _ in 0..turn_count {
let mut events: PendingEventQueue = vec![];
@ -203,7 +204,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
&ds.underlying,
|t, e| e.message(t, current_rec))));
}
external_events(&ds.underlying.mailbox, &account, events)?
external_events(&ds.underlying.mailbox, None, &account, events)?
}
Ok(LinkedTaskTermination::KeepFacet)
});

View File

@ -2,9 +2,10 @@ use structopt::StructOpt;
use syndicate::actor::*;
use syndicate::enclose;
use syndicate::preserves::rec;
use syndicate::relay;
use syndicate::sturdy;
use syndicate::value::Value;
use syndicate::value::NestedValue;
use tokio::net::TcpStream;
@ -20,35 +21,30 @@ pub struct Config {
dataspace: String,
}
#[inline]
fn says(who: AnyValue, what: AnyValue) -> AnyValue {
let mut r = Value::simple_record("Says", 2);
r.fields_vec_mut().push(who);
r.fields_vec_mut().push(what);
r.finish().wrap()
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
syndicate::convenient_logging()?;
let config = Config::from_args();
let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?;
let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split();
Actor::new(None).boot(syndicate::name!("producer"), |t| {
Actor::top(None, |t| {
relay::connect_stream(t, i, o, false, sturdyref, (), move |_state, t, ds| {
let padding: AnyValue = Value::ByteString(vec![0; config.bytes_padding]).wrap();
let padding = AnyValue::new(&vec![0u8; config.bytes_padding][..]);
let action_count = config.action_count;
let account = Account::new(syndicate::name!("account"));
t.linked_task(syndicate::name!("sender"), async move {
let account = Account::new(None, None);
t.linked_task(Some(AnyValue::symbol("sender")), async move {
loop {
account.ensure_clear_funds().await;
let mut events: PendingEventQueue = Vec::new();
for _ in 0..action_count {
events.push(Box::new(enclose!((ds, padding) move |t| t.with_entity(
&ds.underlying, |t, e| e.message(
t, says(Value::from("producer").wrap(), padding))))));
t,
rec![AnyValue::symbol("Says"),
AnyValue::new("producer"),
padding])))));
}
external_events(&ds.underlying.mailbox, &account, events)?;
external_events(&ds.underlying.mailbox, None, &account, events)?;
}
});
Ok(None)

View File

@ -26,7 +26,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::from_args();
let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?;
let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split();
Actor::new(None).boot(syndicate::name!("state-consumer"), |t| {
Actor::top(None, |t| {
relay::connect_stream(t, i, o, false, sturdyref, (), |_state, t, ds| {
let consumer = {
#[derive(Default)]
@ -65,13 +65,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
observer: Arc::clone(&consumer),
});
t.linked_task(syndicate::name!("tick"), async move {
t.linked_task(Some(AnyValue::symbol("tick")), async move {
let mut stats_timer = interval(Duration::from_secs(1));
loop {
stats_timer.tick().await;
let consumer = Arc::clone(&consumer);
external_event(&Arc::clone(&consumer.underlying.mailbox),
&Account::new(syndicate::name!("account")),
None,
&Account::new(None, None),
Box::new(move |t| t.with_entity(
&consumer.underlying,
|t, e| e.message(t, AnyValue::new(true)))))?;

View File

@ -4,9 +4,10 @@ use structopt::StructOpt;
use syndicate::actor::*;
use syndicate::enclose;
use syndicate::preserves::rec;
use syndicate::relay;
use syndicate::sturdy;
use syndicate::value::Value;
use syndicate::value::NestedValue;
use tokio::net::TcpStream;
@ -22,23 +23,21 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::from_args();
let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?;
let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split();
Actor::new(None).boot(syndicate::name!("state-producer"), |t| {
Actor::top(None, |t| {
relay::connect_stream(t, i, o, false, sturdyref, (), move |_state, t, ds| {
let account = Account::new(syndicate::name!("account"));
t.linked_task(syndicate::name!("sender"), async move {
let presence: AnyValue = Value::simple_record1(
"Present",
Value::from(std::process::id()).wrap()).wrap();
let account = Account::new(None, None);
t.linked_task(Some(AnyValue::symbol("sender")), async move {
let presence = rec![AnyValue::symbol("Present"), AnyValue::new(std::process::id())];
let handle = syndicate::actor::next_handle();
let assert_e = || {
external_event(
&Arc::clone(&ds.underlying.mailbox), &account, Box::new(enclose!(
&Arc::clone(&ds.underlying.mailbox), None, &account, Box::new(enclose!(
(ds, presence, handle) move |t| t.with_entity(
&ds.underlying, |t, e| e.assert(t, presence, handle)))))
};
let retract_e = || {
external_event(
&Arc::clone(&ds.underlying.mailbox), &account, Box::new(enclose!(
&Arc::clone(&ds.underlying.mailbox), None, &account, Box::new(enclose!(
(ds, handle) move |t| t.with_entity(
&ds.underlying, |t, e| e.retract(t, handle)))))
};

View File

@ -5,6 +5,7 @@ use std::sync::Arc;
use syndicate::actor::*;
use syndicate::during::entity;
use syndicate::enclose;
use syndicate::preserves::rec;
use syndicate::schemas::dataspace::Observe;
use syndicate::schemas::service;
use syndicate::value::NestedValue;
@ -16,10 +17,10 @@ use crate::schemas::internal_services;
use syndicate_macros::during;
pub fn boot(t: &mut Activation, ds: Arc<Cap>) {
t.spawn(syndicate::name!("dependencies"), move |t| {
t.spawn(Some(AnyValue::symbol("dependencies_listener")), move |t| {
Ok(during!(t, ds, language(), <require-service $spec>, |t: &mut Activation| {
tracing::debug!(?spec, "tracking dependencies");
t.spawn_link(syndicate::name!(parent: None, "dependencies", spec = ?spec),
t.spawn_link(Some(rec![AnyValue::symbol("dependencies"), language().unparse(&spec)]),
enclose!((ds) |t| run(t, ds, spec)));
Ok(())
}))

View File

@ -1,5 +1,6 @@
use preserves_schema::Codec;
use std::io;
use std::path::PathBuf;
use std::sync::Arc;
@ -11,6 +12,7 @@ use syndicate::enclose;
use syndicate::relay;
use syndicate::schemas::service;
use syndicate::schemas::transport_address;
use syndicate::trace;
use syndicate::value::Map;
use syndicate::value::NestedValue;
@ -50,6 +52,9 @@ struct ServerConfig {
#[structopt(long)]
no_banner: bool,
#[structopt(short = "t", long)]
trace_file: Option<PathBuf>,
}
#[tokio::main]
@ -83,13 +88,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing::trace!("startup");
Actor::new(None).boot(tracing::Span::current(), move |t| {
let server_config_ds = Cap::new(&t.create(Dataspace::new(Some(syndicate::name!("config")))));
let log_ds = Cap::new(&t.create(Dataspace::new(Some(syndicate::name!("log")))));
let trace_collector = config.trace_file.clone().map(
|p| Ok::<trace::TraceCollector, io::Error>(trace::TraceCollector::ascii(
io::BufWriter::new(std::fs::File::create(p)?))))
.transpose()?;
Actor::top(trace_collector, move |t| {
let server_config_ds = Cap::new(&t.create(Dataspace::new(Some(AnyValue::symbol("config")))));
let log_ds = Cap::new(&t.create(Dataspace::new(Some(AnyValue::symbol("log")))));
if config.inferior {
tracing::info!("inferior server instance");
t.spawn(syndicate::name!("parent"), enclose!((server_config_ds) move |t| {
t.spawn(Some(AnyValue::symbol("parent")), enclose!((server_config_ds) move |t| {
protocol::run_io_relay(t,
relay::Input::Bytes(Box::pin(tokio::io::stdin())),
relay::Output::Bytes(Box::pin(tokio::io::stdout())),
@ -154,7 +164,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
});
}
t.spawn(tracing::Span::current(), enclose!((log_ds) move |t| {
t.spawn(Some(AnyValue::symbol("logger")), enclose!((log_ds) move |t| {
let n_unknown: AnyValue = AnyValue::symbol("-");
let n_pid: AnyValue = AnyValue::symbol("pid");
let n_line: AnyValue = AnyValue::symbol("line");

View File

@ -9,6 +9,7 @@ use syndicate::actor::*;
use syndicate::error::Error;
use syndicate::error::error;
use syndicate::relay;
use syndicate::trace;
use syndicate::value::NestedValue;
use tokio::net::TcpStream;
@ -37,16 +38,19 @@ pub fn run_io_relay(
}
pub fn run_connection(
trace_collector: Option<trace::TraceCollector>,
facet: FacetRef,
i: relay::Input,
o: relay::Output,
initial_ref: Arc<Cap>,
) {
facet.activate(Account::new(syndicate::name!("start-session")),
|t| run_io_relay(t, i, o, initial_ref));
let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("start-session"));
let account = Account::new(Some(AnyValue::symbol("start-session")), trace_collector);
facet.activate(&account, cause, |t| run_io_relay(t, i, o, initial_ref));
}
pub async fn detect_protocol(
trace_collector: Option<trace::TraceCollector>,
facet: FacetRef,
stream: TcpStream,
gateway: Arc<Cap>,
@ -76,7 +80,7 @@ pub async fn detect_protocol(
_ => unreachable!()
}
};
run_connection(facet, i, o, gateway);
run_connection(trace_collector, facet, i, o, gateway);
Ok(())
}

View File

@ -3,6 +3,8 @@ use notify::Watcher;
use notify::RecursiveMode;
use notify::watcher;
use syndicate::preserves::rec;
use std::fs;
use std::future;
use std::io;
@ -16,6 +18,7 @@ use syndicate::actor::*;
use syndicate::error::Error;
use syndicate::enclose;
use syndicate::supervise::{Supervisor, SupervisorConfiguration};
use syndicate::trace;
use syndicate::value::BinarySource;
use syndicate::value::BytesBinarySource;
use syndicate::value::Map;
@ -32,11 +35,11 @@ use crate::script;
use syndicate_macros::during;
pub fn on_demand(t: &mut Activation, config_ds: Arc<Cap>) {
t.spawn(syndicate::name!("config_watcher"), move |t| {
t.spawn(Some(AnyValue::symbol("config_watcher")), move |t| {
Ok(during!(t, config_ds, language(), <run-service $spec: internal_services::ConfigWatcher>, |t| {
Supervisor::start(
t,
syndicate::name!(parent: None, "config", path = ?spec.path),
Some(rec![AnyValue::symbol("config"), AnyValue::new(spec.path.clone())]),
SupervisorConfiguration::default(),
enclose!((config_ds, spec) lifecycle::updater(config_ds, spec)),
enclose!((config_ds) move |t| enclose!((config_ds, spec) run(t, config_ds, spec))))
@ -175,26 +178,32 @@ fn run(
watcher.watch(&env.path, RecursiveMode::Recursive).map_err(convert_notify_error)?;
let facet = t.facet.clone();
let trace_collector = t.trace_collector();
let span = tracing::Span::current();
thread::spawn(move || {
let _entry = span.enter();
let mut path_state: Map<PathBuf, FacetId> = Map::new();
if !facet.activate(
Account::new(syndicate::name!("initial_scan")),
|t| {
initial_scan(t, &mut path_state, &config_ds, env.clone());
config_ds.assert(t, language(), &lifecycle::ready(&spec));
Ok(())
})
{
return;
let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("initial_scan"));
let account = Account::new(Some(AnyValue::symbol("initial_scan")), trace_collector.clone());
if !facet.activate(
&account, cause, |t| {
initial_scan(t, &mut path_state, &config_ds, env.clone());
config_ds.assert(t, language(), &lifecycle::ready(&spec));
Ok(())
})
{
return;
}
}
tracing::trace!("initial_scan complete");
let mut rescan = |paths: Vec<PathBuf>| {
facet.activate(Account::new(syndicate::name!("rescan")), |t| {
let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("rescan"));
let account = Account::new(Some(AnyValue::symbol("rescan")), trace_collector.clone());
facet.activate(&account, cause, |t| {
let mut to_stop = Vec::new();
for path in paths.into_iter() {
let maybe_facet_id = path_state.remove(&path);
@ -236,15 +245,19 @@ fn run(
if !keep_running { break; }
}
facet.activate(Account::new(syndicate::name!("termination")), |t| {
tracing::trace!("linked thread terminating associated facet");
Ok(t.stop())
});
{
let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("termination"));
let account = Account::new(Some(AnyValue::symbol("termination")), trace_collector);
facet.activate(&account, cause, |t| {
tracing::trace!("linked thread terminating associated facet");
Ok(t.stop())
});
}
tracing::trace!("linked thread done");
});
t.linked_task(syndicate::name!("cancel-wait"), async move {
t.linked_task(Some(AnyValue::symbol("cancel-wait")), async move {
future::pending::<()>().await;
drop(watcher);
Ok(LinkedTaskTermination::KeepFacet)

View File

@ -4,8 +4,10 @@ use std::sync::Arc;
use syndicate::actor::*;
use syndicate::enclose;
use syndicate::preserves::rec;
use syndicate::schemas::service;
use syndicate::supervise::{Supervisor, SupervisorConfiguration};
use syndicate::trace;
use syndicate::value::NestedValue;
use tokio::io::AsyncRead;
@ -21,7 +23,7 @@ use crate::schemas::external_services::*;
use syndicate_macros::during;
pub fn on_demand(t: &mut Activation, config_ds: Arc<Cap>, root_ds: Arc<Cap>) {
t.spawn(syndicate::name!("daemon"), move |t| {
t.spawn(Some(AnyValue::symbol("daemon_listener")), move |t| {
Ok(during!(t, config_ds, language(), <run-service $spec: DaemonService>,
enclose!((config_ds, root_ds) move |t: &mut Activation| {
supervise_daemon(t, config_ds, root_ds, spec)
@ -46,7 +48,7 @@ fn supervise_daemon(
}));
Supervisor::start(
t,
syndicate::name!(parent: None, "daemon", id = ?spec.id),
Some(language().unparse(&spec)),
SupervisorConfiguration::on_error_only(),
enclose!((config_ds, spec) lifecycle::updater(config_ds, spec)),
enclose!((config_ds, root_ds) move |t|
@ -162,7 +164,6 @@ struct DaemonInstance {
config_ds: Arc<Cap>,
log_ds: Arc<Cap>,
service: AnyValue,
name: tracing::Span,
cmd: process::Command,
announce_presumed_readiness: bool,
unready_configs: Arc<Field<isize>>,
@ -236,9 +237,13 @@ impl DaemonInstance {
Some(n) => AnyValue::new(n),
None => AnyValue::symbol("unknown"),
};
t.spawn(syndicate::name!(parent: self.name.clone(), "log"), move |t| {
t.linked_task(tracing::Span::current(), async move {
let trace_collector = t.trace_collector();
t.spawn(Some(rec![AnyValue::symbol("log"), kind.clone(), self.service.clone()]), move |t| {
t.linked_task(None, async move {
let mut r = BufReader::new(r);
let cause = trace_collector.as_ref().map(
|_| trace::TurnCause::external(kind.value().as_symbol().unwrap()));
let account = Account::new(None, trace_collector);
loop {
let mut buf = Vec::new();
if r.read_until(b'\n', &mut buf).await? == 0 {
@ -250,8 +255,7 @@ impl DaemonInstance {
};
let now = AnyValue::new(chrono::Utc::now().to_rfc3339());
if !facet.activate(
Account::new(tracing::Span::current()),
enclose!((pid, service, kind) |t| {
&account, cause.clone(), enclose!((pid, service, kind) |t| {
log_ds.message(t, &(), &syndicate_macros::template!(
"<log =now {
pid: =pid,
@ -304,13 +308,17 @@ impl DaemonInstance {
counter::adjust(t, &self.unready_configs, -1);
}
let trace_collector = t.trace_collector();
t.linked_task(
syndicate::name!(parent: self.name.clone(), "wait"),
Some(rec![AnyValue::symbol("wait"), self.service.clone()]),
enclose!((facet) async move {
tracing::trace!("waiting for process exit");
let status = child.wait().await?;
tracing::debug!(?status);
facet.activate(Account::new(syndicate::name!("instance-terminated")), |t| {
let cause = trace_collector.as_ref().map(
|_| trace::TurnCause::external("instance-terminated"));
let account = Account::new(Some(AnyValue::symbol("instance-terminated")), trace_collector);
facet.activate(&account, cause, |t| {
let m = if status.success() { None } else { Some(format!("{}", status)) };
self.handle_exit(t, m)
});
@ -378,9 +386,10 @@ fn run(
Ok(())
}))?;
let trace_collector = t.trace_collector();
enclose!((config_ds, unready_configs, completed_processes)
during!(t, config_ds.clone(), language(), <daemon #(&service.id) $config>, {
enclose!((spec, config_ds, root_ds, unready_configs, completed_processes)
enclose!((spec, config_ds, root_ds, unready_configs, completed_processes, trace_collector)
|t: &mut Activation| {
tracing::debug!(?config, "new config");
counter::adjust(t, &unready_configs, 1);
@ -391,7 +400,7 @@ fn run(
tracing::info!(?config);
let config = config.elaborate();
let facet = t.facet.clone();
t.linked_task(syndicate::name!("subprocess"), async move {
t.linked_task(Some(AnyValue::symbol("subprocess")), async move {
let mut cmd = config.process.build_command().ok_or("Cannot start daemon process")?;
let announce_presumed_readiness = match config.ready_on_start {
@ -432,7 +441,6 @@ fn run(
config_ds,
log_ds: root_ds,
service: spec,
name: tracing::Span::current(),
cmd,
announce_presumed_readiness,
unready_configs,
@ -441,7 +449,10 @@ fn run(
protocol,
};
facet.activate(Account::new(syndicate::name!("instance-startup")), |t| {
let cause = trace_collector.as_ref().map(
|_| trace::TurnCause::external("instance-startup"));
let account = Account::new(Some(AnyValue::symbol("instance-startup")), trace_collector);
facet.activate(&account, cause, |t| {
daemon_instance.start(t)
});
Ok(LinkedTaskTermination::KeepFacet)

View File

@ -1,7 +1,11 @@
use preserves_schema::Codec;
use std::sync::Arc;
use syndicate::actor::*;
use syndicate::enclose;
use syndicate::preserves::rec;
use syndicate::preserves::value::NestedValue;
use crate::language::language;
use crate::lifecycle;
@ -10,9 +14,10 @@ use crate::schemas::internal_services::DebtReporter;
use syndicate_macros::during;
pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) {
t.spawn(syndicate::name!("debt_reporter"), move |t| {
t.spawn(Some(AnyValue::symbol("debt_reporter_listener")), move |t| {
Ok(during!(t, ds, language(), <run-service $spec: DebtReporter>, |t: &mut Activation| {
t.spawn_link(tracing::Span::current(), enclose!((ds) |t| run(t, ds, spec)));
t.spawn_link(Some(rec![AnyValue::symbol("debt_reporter"), language().unparse(&spec)]),
enclose!((ds) |t| run(t, ds, spec)));
Ok(())
}))
});
@ -23,8 +28,7 @@ fn run(t: &mut Activation, ds: Arc<Cap>, spec: DebtReporter) -> ActorResult {
ds.assert(t, language(), &lifecycle::ready(&spec));
t.every(core::time::Duration::from_millis((spec.interval_seconds.0 * 1000.0) as u64), |_t| {
for (id, (name, debt)) in syndicate::actor::ACCOUNTS.read().iter() {
let _enter = name.enter();
tracing::info!(id, debt = ?debt.load(std::sync::atomic::Ordering::Relaxed));
tracing::info!(id, ?name, debt = ?debt.load(std::sync::atomic::Ordering::Relaxed));
}
Ok(())
})

View File

@ -1,7 +1,10 @@
use preserves_schema::Codec;
use std::sync::Arc;
use syndicate::actor::*;
use syndicate::enclose;
use syndicate::preserves::value::NestedValue;
use syndicate::supervise::{Supervisor, SupervisorConfiguration};
use crate::language::language;
@ -11,11 +14,11 @@ use crate::schemas::internal_services::Milestone;
use syndicate_macros::during;
pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) {
t.spawn(syndicate::name!("milestone"), move |t| {
t.spawn(Some(AnyValue::symbol("milestone_listener")), move |t| {
Ok(during!(t, ds, language(), <run-service $spec: Milestone>, |t: &mut Activation| {
Supervisor::start(
t,
syndicate::name!(parent: None, "milestone", name = ?spec.name),
Some(language().unparse(&spec)),
SupervisorConfiguration::default(),
|_, _| Ok(()),
enclose!((ds) move |t| enclose!((ds, spec) run(t, ds, spec))))

View File

@ -1,9 +1,14 @@
use preserves_schema::Codec;
use std::convert::TryFrom;
use std::sync::Arc;
use syndicate::actor::*;
use syndicate::enclose;
use syndicate::preserves::rec;
use syndicate::preserves::value::NestedValue;
use syndicate::supervise::{Supervisor, SupervisorConfiguration};
use syndicate::trace;
use tokio::net::TcpListener;
@ -15,11 +20,11 @@ use crate::schemas::internal_services::TcpRelayListener;
use syndicate_macros::during;
pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) {
t.spawn(syndicate::name!("tcp_relay_listener"), move |t| {
t.spawn(Some(AnyValue::symbol("tcp_relay_listener")), move |t| {
Ok(during!(t, ds, language(), <run-service $spec: TcpRelayListener>, |t| {
Supervisor::start(
t,
syndicate::name!(parent: None, "relay", addr = ?spec),
Some(rec![AnyValue::symbol("relay"), language().unparse(&spec)]),
SupervisorConfiguration::default(),
enclose!((ds, spec) lifecycle::updater(ds, spec)),
enclose!((ds) move |t| enclose!((ds, spec) run(t, ds, spec))))
@ -31,40 +36,45 @@ fn run(t: &mut Activation, ds: Arc<Cap>, spec: TcpRelayListener) -> ActorResult
lifecycle::terminate_on_service_restart(t, &ds, &spec);
let host = spec.addr.host.clone();
let port = u16::try_from(&spec.addr.port).map_err(|_| "Invalid TCP port number")?;
let parent_span = tracing::Span::current();
let facet = t.facet.clone();
t.linked_task(syndicate::name!("listener"), async move {
let trace_collector = t.trace_collector();
t.linked_task(Some(AnyValue::symbol("listener")), async move {
let listen_addr = format!("{}:{}", host, port);
let listener = TcpListener::bind(listen_addr).await?;
if !facet.activate(
Account::new(syndicate::name!("readiness")), |t| {
tracing::info!("listening");
ds.assert(t, language(), &lifecycle::ready(&spec));
Ok(())
})
{
return Ok(LinkedTaskTermination::Normal);
let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("readiness"));
let account = Account::new(Some(AnyValue::symbol("readiness")), trace_collector.clone());
if !facet.activate(
&account, cause, |t| {
tracing::info!("listening");
ds.assert(t, language(), &lifecycle::ready(&spec));
Ok(())
})
{
return Ok(LinkedTaskTermination::Normal);
}
}
loop {
let (stream, addr) = listener.accept().await?;
let gatekeeper = spec.gatekeeper.clone();
let name = syndicate::name!(parent: parent_span.clone(), "conn");
let name = Some(rec![AnyValue::symbol("tcp"), AnyValue::new(format!("{}", &addr))]);
let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("connect"));
let account = Account::new(name.clone(), trace_collector.clone());
if !facet.activate(
Account::new(name.clone()),
move |t| {
&account, cause, enclose!((trace_collector) move |t| {
t.spawn(name, move |t| {
Ok(t.linked_task(tracing::Span::current(), {
Ok(t.linked_task(None, {
let facet = t.facet.clone();
async move {
detect_protocol(facet, stream, gatekeeper, addr).await?;
detect_protocol(trace_collector, facet, stream, gatekeeper, addr).await?;
Ok(LinkedTaskTermination::KeepFacet)
}
}))
});
Ok(())
})
}))
{
return Ok(LinkedTaskTermination::Normal);
}

View File

@ -1,3 +1,5 @@
use preserves_schema::Codec;
use std::io;
use std::path::PathBuf;
use std::sync::Arc;
@ -5,8 +7,11 @@ use std::sync::Arc;
use syndicate::actor::*;
use syndicate::enclose;
use syndicate::error::Error;
use syndicate::preserves::rec;
use syndicate::preserves::value::NestedValue;
use syndicate::relay;
use syndicate::supervise::{Supervisor, SupervisorConfiguration};
use syndicate::trace;
use tokio::net::UnixListener;
use tokio::net::UnixStream;
@ -19,11 +24,11 @@ use crate::schemas::internal_services::UnixRelayListener;
use syndicate_macros::during;
pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) {
t.spawn(syndicate::name!("unix_relay_listener"), move |t| {
t.spawn(Some(AnyValue::symbol("unix_relay_listener")), move |t| {
Ok(during!(t, ds, language(), <run-service $spec: UnixRelayListener>, |t| {
Supervisor::start(
t,
syndicate::name!(parent: None, "relay", addr = ?spec),
Some(rec![AnyValue::symbol("relay"), language().unparse(&spec)]),
SupervisorConfiguration::default(),
enclose!((ds, spec) lifecycle::updater(ds, spec)),
enclose!((ds) move |t| enclose!((ds, spec) run(t, ds, spec))))
@ -34,38 +39,44 @@ pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) {
fn run(t: &mut Activation, ds: Arc<Cap>, spec: UnixRelayListener) -> ActorResult {
lifecycle::terminate_on_service_restart(t, &ds, &spec);
let path_str = spec.addr.path.clone();
let parent_span = tracing::Span::current();
let facet = t.facet.clone();
t.linked_task(syndicate::name!("listener"), async move {
let trace_collector = t.trace_collector();
t.linked_task(Some(AnyValue::symbol("listener")), async move {
let listener = bind_unix_listener(&PathBuf::from(path_str)).await?;
if !facet.activate(
Account::new(syndicate::name!("readiness")), |t| {
tracing::info!("listening");
ds.assert(t, language(), &lifecycle::ready(&spec));
Ok(())
})
{
return Ok(LinkedTaskTermination::Normal);
let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("readiness"));
let account = Account::new(Some(AnyValue::symbol("readiness")), trace_collector.clone());
if !facet.activate(
&account, cause, |t| {
tracing::info!("listening");
ds.assert(t, language(), &lifecycle::ready(&spec));
Ok(())
})
{
return Ok(LinkedTaskTermination::Normal);
}
}
loop {
let (stream, _addr) = listener.accept().await?;
let peer = stream.peer_cred()?;
let gatekeeper = spec.gatekeeper.clone();
let name = syndicate::name!(parent: parent_span.clone(), "conn",
pid = ?peer.pid().unwrap_or(-1),
uid = peer.uid());
let name = Some(rec![AnyValue::symbol("unix"),
AnyValue::new(peer.pid().unwrap_or(-1)),
AnyValue::new(peer.uid())]);
let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("connect"));
let account = Account::new(name.clone(), trace_collector.clone());
if !facet.activate(
Account::new(name.clone()),
move |t| {
&account, cause, enclose!((trace_collector) move |t| {
t.spawn(name, |t| {
Ok(t.linked_task(tracing::Span::current(), {
Ok(t.linked_task(None, {
let facet = t.facet.clone();
async move {
tracing::info!(protocol = %"unix");
let (i, o) = stream.into_split();
run_connection(facet,
run_connection(trace_collector,
facet,
relay::Input::Bytes(Box::pin(i)),
relay::Output::Bytes(Box::pin(o)),
gatekeeper);
@ -74,7 +85,7 @@ fn run(t: &mut Activation, ds: Arc<Cap>, spec: UnixRelayListener) -> ActorResult
}))
});
Ok(())
})
}))
{
return Ok(LinkedTaskTermination::Normal);
}

View File

@ -52,19 +52,19 @@ pub fn bench_pub(c: &mut Criterion) {
b.iter_custom(|iters| {
let start = Instant::now();
rt.block_on(async move {
Actor::new(None).boot(syndicate::name!("dataspace"), move |t| {
Actor::top(None, move |t| {
let ds = t.create(Dataspace::new(None));
let shutdown = t.create(ShutdownEntity);
let account = Account::new(syndicate::name!("sender-account"));
t.linked_task(syndicate::name!("sender"), async move {
let account = Account::new(None, None);
t.linked_task(Some(AnyValue::symbol("sender")), async move {
for _ in 0..iters {
external_event(&ds.mailbox, &account, Box::new(
external_event(&ds.mailbox, None, &account, Box::new(
enclose!((ds) move |t| t.with_entity(
&ds,
|t, e| e.message(t, says(AnyValue::new("bench_pub"),
Value::ByteString(vec![]).wrap()))))))?
}
external_event(&shutdown.mailbox, &account, Box::new(
external_event(&shutdown.mailbox, None, &account, Box::new(
enclose!((shutdown) move |t| t.with_entity(
&shutdown,
|t, e| e.message(t, AnyValue::new(true))))))?;
@ -83,7 +83,7 @@ pub fn bench_pub(c: &mut Criterion) {
rt.block_on(async move {
let turn_count = Arc::new(AtomicU64::new(0));
Actor::new(None).boot(syndicate::name!("dataspace"), {
Actor::top(None, {
let iters = iters.clone();
let turn_count = Arc::clone(&turn_count);
@ -103,7 +103,7 @@ pub fn bench_pub(c: &mut Criterion) {
observer: shutdown,
});
t.spawn(syndicate::name!("consumer"), move |t| {
t.spawn(Some(AnyValue::symbol("consumer")), move |t| {
struct Receiver(Arc<AtomicU64>);
impl Entity<AnyValue> for Receiver {
fn message(&mut self, _t: &mut Activation, _m: AnyValue) -> ActorResult {
@ -139,10 +139,11 @@ pub fn bench_pub(c: &mut Criterion) {
});
let account = Arc::clone(t.account());
t.linked_task(syndicate::name!("sender"), async move {
t.linked_task(Some(AnyValue::symbol("sender")), async move {
for _i in 0..iters {
let ds = Arc::clone(&ds);
external_event(&Arc::clone(&ds.underlying.mailbox), &account, Box::new(
external_event(
&Arc::clone(&ds.underlying.mailbox), None, &account, Box::new(
move |t| t.with_entity(
&ds.underlying,
|t, e| e.message(t, says(AnyValue::new("bench_pub"),
@ -150,7 +151,8 @@ pub fn bench_pub(c: &mut Criterion) {
}
{
let ds = Arc::clone(&ds);
external_event(&Arc::clone(&ds.underlying.mailbox), &account, Box::new(
external_event(
&Arc::clone(&ds.underlying.mailbox), None, &account, Box::new(
move |t| t.with_entity(
&ds.underlying,
|t, e| e.message(t, AnyValue::new(true)))))?;

View File

@ -7,6 +7,8 @@ use std::time::Duration;
use std::time::Instant;
use syndicate::actor::*;
use syndicate::preserves::rec;
use syndicate::value::NestedValue;
use tokio::runtime::Runtime;
@ -88,14 +90,16 @@ pub fn bench_ring(c: &mut Criterion) {
self.i += 1;
let spawner_ref = Arc::clone(&self.self_ref);
ACTORS_CREATED.fetch_add(1, Ordering::Relaxed);
t.spawn(syndicate::name!("forwarder", ?i), move |t| {
let _ = t.prevent_inert_check();
let f = t.create(Forwarder {
next,
t.spawn(
Some(rec![AnyValue::symbol("forwarder"), AnyValue::new(i)]),
move |t| {
let _ = t.prevent_inert_check();
let f = t.create(Forwarder {
next,
});
t.message(&spawner_ref, f);
Ok(())
});
t.message(&spawner_ref, f);
Ok(())
});
} else {
let mut c_state = Counter {
start: Instant::now(),
@ -118,7 +122,7 @@ pub fn bench_ring(c: &mut Criterion) {
}
ACTORS_CREATED.fetch_add(1, Ordering::Relaxed);
Actor::new(None).boot(syndicate::name!("counter"), move |t| {
Actor::top(None, move |t| {
let _ = t.prevent_inert_check();
let mut s = Spawner {
self_ref: t.create_inert(),

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@ use preserves_schema::Codec;
/// A Dataspace object (entity).
#[derive(Debug)]
pub struct Dataspace {
pub name: tracing::Span,
pub name: Name,
/// Index over assertions placed in the dataspace; used to
/// efficiently route assertion changes and messages to observers.
pub index: skeleton::Index,
@ -31,10 +31,9 @@ pub struct Dataspace {
impl Dataspace {
/// Construct a new, empty dataspace.
pub fn new(name: Option<tracing::Span>) -> Self {
pub fn new(name: Name) -> Self {
Self {
name: name.map_or_else(|| crate::name!("anonymous_dataspace"),
|n| crate::name!(parent: &n, "dataspace")),
name: name,
index: skeleton::Index::new(),
handle_map: Map::new(),
}
@ -62,10 +61,8 @@ impl Dataspace {
impl Entity<_Any> for Dataspace {
fn assert(&mut self, t: &mut Activation, a: _Any, h: Handle) -> ActorResult {
let _guard = self.name.enter();
let is_new = self.index.insert(t, &a);
tracing::trace!(assertion = ?a, handle = ?h, ?is_new, "assert");
tracing::trace!(dataspace = ?self.name, assertion = ?a, handle = ?h, ?is_new, "assert");
if is_new {
if let Ok(o) = language().parse::<Observe>(&a) {
@ -78,13 +75,11 @@ impl Entity<_Any> for Dataspace {
}
fn retract(&mut self, t: &mut Activation, h: Handle) -> ActorResult {
let _guard = self.name.enter();
match self.handle_map.remove(&h) {
None => tracing::warn!(handle = ?h, "retract of unknown handle"),
None => tracing::warn!(dataspace = ?self.name, handle = ?h, "retract of unknown handle"),
Some(a) => {
let is_last = self.index.remove(t, &a);
tracing::trace!(assertion = ?a, handle = ?h, ?is_last, "retract");
tracing::trace!(dataspace = ?self.name, assertion = ?a, handle = ?h, ?is_last, "retract");
if is_last {
if let Ok(o) = language().parse::<Observe>(&a) {
@ -97,9 +92,7 @@ impl Entity<_Any> for Dataspace {
}
fn message(&mut self, t: &mut Activation, m: _Any) -> ActorResult {
let _guard = self.name.enter();
tracing::trace!(body = ?m, "message");
tracing::trace!(dataspace = ?self.name, body = ?m, "message");
self.index.send(t, &m);
Ok(())
}

View File

@ -1,4 +1,5 @@
#![doc = include_str!("../README.md")]
#![feature(min_specialization)]
#[doc(inline)]
pub use preserves::value;
@ -29,13 +30,33 @@ pub mod schemas {
pub mod skeleton;
pub mod sturdy;
pub mod tracer;
pub mod trace;
#[doc(inline)]
pub use during::entity;
#[doc(inline)]
pub use tracer::convenient_logging;
/// Sets up [`tracing`] logging in a reasonable way.
///
/// Useful at the top of `main` functions.
pub fn convenient_logging() -> Result<(), Box<dyn std::error::Error>> {
let filter = match std::env::var(tracing_subscriber::filter::EnvFilter::DEFAULT_ENV) {
Err(std::env::VarError::NotPresent) =>
tracing_subscriber::filter::EnvFilter::default()
.add_directive(tracing_subscriber::filter::LevelFilter::INFO.into()),
_ =>
tracing_subscriber::filter::EnvFilter::try_from_default_env()?,
};
let subscriber = tracing_subscriber::fmt()
.with_ansi(true)
.with_thread_ids(true)
.with_max_level(tracing::Level::TRACE)
.with_env_filter(filter)
.with_writer(std::io::stderr)
.finish();
tracing::subscriber::set_global_default(subscriber)
.expect("Could not set tracing global subscriber");
Ok(())
}
preserves_schema::define_language!(language(): Language<actor::AnyValue> {
syndicate: schemas::Language,

View File

@ -9,6 +9,7 @@ use crate::error::error;
use crate::schemas::gatekeeper;
use crate::schemas::protocol as P;
use crate::schemas::sturdy;
use crate::trace;
use futures::Sink;
use futures::SinkExt;
@ -248,8 +249,10 @@ impl TunnelRelay {
|io| Arc::clone(&tr.membranes.import_oid(t, &tr_ref, io).inc_ref().obj));
dump_membranes!(tr.membranes);
*tr_ref.lock() = Some(tr);
t.linked_task(crate::name!("writer"), output_loop(o, output_rx));
t.linked_task(crate::name!("reader"), input_loop(t.facet.clone(), i, tr_ref));
t.linked_task(Some(AnyValue::symbol("writer")),
output_loop(o, output_rx));
t.linked_task(Some(AnyValue::symbol("reader")),
input_loop(t.trace_collector(), t.facet.clone(), i, tr_ref));
t.state.add_exit_hook(&self_entity);
result
}
@ -619,11 +622,13 @@ impl DomainEncode<Arc<Cap>> for Membranes {
}
async fn input_loop(
trace_collector: Option<trace::TraceCollector>,
facet: FacetRef,
i: Input,
relay: TunnelRelayRef,
) -> Result<LinkedTaskTermination, Error> {
let account = Account::new(crate::name!("input-loop"));
let account = Account::new(Some(AnyValue::symbol("input-loop")), trace_collector);
let cause = trace::TurnCause::external("input-loop");
match i {
Input::Packets(mut src) => {
loop {
@ -631,12 +636,15 @@ async fn input_loop(
match src.next().await {
None => break,
Some(bs) => {
let is_alive = facet.activate(Arc::clone(&account), |t| {
let mut g = relay.lock();
let tr = g.as_mut().expect("initialized");
tr.handle_inbound_datagram(t, &bs?)
});
if !is_alive { break; }
if !facet.activate(
&account, Some(cause.clone()), |t| {
let mut g = relay.lock();
let tr = g.as_mut().expect("initialized");
tr.handle_inbound_datagram(t, &bs?)
})
{
break;
}
}
}
}
@ -659,12 +667,15 @@ async fn input_loop(
match n {
0 => break,
_ => {
let is_alive = facet.activate(Arc::clone(&account), |t| {
let mut g = relay.lock();
let tr = g.as_mut().expect("initialized");
tr.handle_inbound_stream(t, &mut buf)
});
if !is_alive { break; }
if !facet.activate(
&account, Some(cause.clone()), |t| {
let mut g = relay.lock();
let tr = g.as_mut().expect("initialized");
tr.handle_inbound_stream(t, &mut buf)
})
{
break;
}
}
}
}

View File

@ -1,6 +1,8 @@
//! Extremely simple single-actor supervision. Vastly simplified compared to the available
//! options in [Erlang/OTP](https://erlang.org/doc/man/supervisor.html).
use preserves::value::NestedValue;
use std::collections::VecDeque;
use std::sync::Arc;
use std::time::Duration;
@ -8,7 +10,6 @@ use std::time::Duration;
use tokio::time::Instant;
use crate::actor::*;
use crate::enclose;
use crate::schemas::service::State;
pub type Boot = Box<dyn Send + FnMut(&mut Activation) -> ActorResult>;
@ -36,7 +37,8 @@ pub struct SupervisorConfiguration {
pub struct Supervisor {
self_ref: Arc<Ref<Protocol>>,
name: tracing::Span,
my_name: Name,
child_name: Name,
config: SupervisorConfiguration,
boot_fn: Option<Boot>,
restarts: VecDeque<Instant>,
@ -86,8 +88,7 @@ impl Entity<Protocol> for Supervisor
}
fn retract(&mut self, t: &mut Activation, _h: Handle) -> ActorResult {
let _name = self.name.clone();
let _entry = _name.enter();
let _entry = tracing::info_span!("supervisor", name = ?self.child_name).entered();
let exit_status =
self.ac_ref.take().expect("valid supervisee ActorRef")
.exit_status()
@ -144,8 +145,9 @@ impl Entity<Protocol> for Supervisor
}
fn stop(&mut self, _t: &mut Activation) -> ActorResult {
let _entry = self.name.enter();
tracing::info!(self_ref = ?self.self_ref, "Supervisor terminating");
tracing::info!(name = ?self.my_name,
self_ref = ?self.self_ref,
"Supervisor terminating");
Ok(())
}
}
@ -154,18 +156,21 @@ impl Supervisor {
pub fn start<C: 'static + Send + FnMut(&mut Activation, State) -> ActorResult,
B: 'static + Send + FnMut(&mut Activation) -> ActorResult>(
t: &mut Activation,
name: tracing::Span,
name: Name,
config: SupervisorConfiguration,
mut state_cb: C,
boot_fn: B,
) -> ActorResult {
let _entry = name.enter();
let _entry = tracing::info_span!("supervisor", ?name).entered();
tracing::trace!(?config);
let self_ref = t.create_inert();
let state_field = t.named_field("supervisee_state", State::Started);
let my_name = name.as_ref().map(
|n| preserves::rec![AnyValue::symbol("supervisor"), n.clone()]);
let mut supervisor = Supervisor {
self_ref: Arc::clone(&self_ref),
name: name.clone(),
my_name: my_name.clone(),
child_name: name,
config,
boot_fn: Some(Box::new(boot_fn)),
restarts: VecDeque::new(),
@ -174,14 +179,11 @@ impl Supervisor {
};
tracing::info!(self_ref = ?supervisor.self_ref, "Supervisor starting");
supervisor.ensure_started(t)?;
t.dataflow(enclose!((name) move |t| {
t.dataflow(move |t| {
let state = t.get(&state_field).clone();
{
let _entry = name.enter();
tracing::debug!(?state);
}
tracing::debug!(name = ?my_name, ?state);
state_cb(t, state)
}))?;
})?;
self_ref.become_entity(supervisor);
t.on_stop_notify(&self_ref);
Ok(())
@ -190,16 +192,16 @@ impl Supervisor {
fn ensure_started(&mut self, t: &mut Activation) -> ActorResult {
match self.boot_fn.take() {
None => {
let _entry = self.name.enter();
t.set(&self.state, State::Failed);
tracing::error!("Cannot restart supervisee, because it panicked at startup")
tracing::error!(name = ?self.my_name,
"Cannot restart supervisee, because it panicked at startup")
}
Some(mut boot_fn) => {
let self_ref = Arc::clone(&self.self_ref);
t.facet(|t: &mut Activation| {
t.assert(&self.self_ref, Protocol::SuperviseeStarted);
self.ac_ref = Some(t.spawn_link(
crate::name!(parent: &self.name, "supervisee"),
self.child_name.clone(),
move |t| {
match boot_fn(t) {
Ok(()) => {

187
syndicate/src/trace.rs Normal file
View File

@ -0,0 +1,187 @@
//! Records *describing* actions committed at the end of a turn and
//! events triggering the start of a turn. These are not the actions
//! or events themselves: they are reflective information on the
//! action of the system, enough to reconstruct interesting
//! projections of system activity.
pub use super::schemas::trace::*;
use preserves::value::NestedValue;
use preserves::value::Writer;
use preserves_schema::Codec;
use super::actor::{self, AnyValue, Ref, Cap};
use super::language;
use std::sync::Arc;
use std::time::SystemTime;
use tokio::select;
use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
#[derive(Debug, Clone)]
pub struct TraceCollector {
pub tx: UnboundedSender<TraceEntry>,
}
impl<M> From<&Ref<M>> for Target {
fn from(v: &Ref<M>) -> Target {
Target {
actor: ActorId(AnyValue::new(v.mailbox.actor_id)),
facet: FacetId(AnyValue::new(u64::from(v.facet_id))),
oid: Oid(AnyValue::new(v.oid())),
}
}
}
impl<M: std::fmt::Debug> From<&M> for AssertionDescription {
default fn from(v: &M) -> Self {
Self::Opaque { description: AnyValue::new(format!("{:?}", v)) }
}
}
impl From<&AnyValue> for AssertionDescription {
fn from(v: &AnyValue) -> Self {
Self::Value { value: v.clone() }
}
}
impl TraceCollector {
pub fn record(&self, id: actor::ActorId, a: ActorActivation) {
let _ = self.tx.send(TraceEntry {
timestamp: SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
.expect("Time after Unix epoch").as_secs_f64().into(),
actor: ActorId(AnyValue::new(id)),
item: a,
});
}
}
impl TurnDescription {
pub fn new(activation_id: u64, cause: TurnCause) -> Self {
Self {
id: TurnId(AnyValue::new(activation_id)),
cause,
actions: Vec::new(),
}
}
pub fn record(&mut self, a: ActionDescription) {
self.actions.push(a)
}
pub fn take(&mut self) -> Self {
Self {
id: self.id.clone(),
cause: self.cause.clone(),
actions: std::mem::take(&mut self.actions),
}
}
}
impl TurnCause {
pub fn external(description: &str) -> Self {
Self::External { description: AnyValue::new(description) }
}
}
struct CapEncoder;
impl preserves::value::DomainEncode<Arc<Cap>> for CapEncoder {
fn encode_embedded<W: Writer>(
&mut self,
w: &mut W,
d: &Arc<Cap>,
) -> std::io::Result<()> {
w.write_string(&d.debug_str())
// use preserves::value::writer::CompoundWriter;
// use preserves::value::boundary as B;
// let mut c = w.start_record(Some(3))?;
// let mut b = B::start(B::Item::RecordLabel);
// c.boundary(&b)?;
// c.write_symbol("cap")?;
// b.shift(Some(B::Item::RecordField));
// c.boundary(&b)?;
// c.write_u64(d.underlying.mailbox.actor_id)?;
// b.shift(Some(B::Item::RecordField));
// c.boundary(&b)?;
// c.write_u64(u64::from(d.underlying.facet_id))?;
// b.shift(Some(B::Item::RecordField));
// c.boundary(&b)?;
// c.write_u64(d.underlying.oid() as u64)?;
// // TODO: write attenuation?
// b.shift(None);
// c.boundary(&b)?;
// w.end_record(c)
}
}
pub enum CollectorEvent {
Event(TraceEntry),
PeriodicFlush,
}
impl TraceCollector {
pub fn new<F: 'static + Send + FnMut(CollectorEvent)>(mut f: F) -> TraceCollector {
let (tx, mut rx) = unbounded_channel::<TraceEntry>();
tokio::spawn(async move {
let mut timer = tokio::time::interval(std::time::Duration::from_secs(1));
loop {
select! {
maybe_entry = rx.recv() => {
match maybe_entry {
None => break,
Some(entry) => {
tracing::trace!(?entry);
f(CollectorEvent::Event(entry));
}
}
},
_ = timer.tick() => f(CollectorEvent::PeriodicFlush),
}
}
});
TraceCollector { tx }
}
pub fn ascii<W: 'static + std::io::Write + Send>(w: W) -> TraceCollector {
let mut writer = preserves::value::TextWriter::new(w);
Self::new(move |event| match event {
CollectorEvent::Event(entry) => {
writer.write(&mut CapEncoder, &language().unparse(&entry))
.expect("failed to write TraceCollector entry");
writer.borrow_write().write_all(b"\n")
.expect("failed to write TraceCollector newline");
},
CollectorEvent::PeriodicFlush =>
writer.flush().expect("failed to flush TraceCollector output"),
})
}
pub fn packed<W: 'static + std::io::Write + Send>(w: W) -> TraceCollector {
let mut writer = preserves::value::PackedWriter::new(w);
Self::new(move |event| match event {
CollectorEvent::Event(entry) =>
writer.write(&mut CapEncoder, &language().unparse(&entry))
.expect("failed to write TraceCollector entry"),
CollectorEvent::PeriodicFlush =>
writer.flush().expect("failed to flush TraceCollector output"),
})
}
}
impl From<actor::Name> for Name {
fn from(v: actor::Name) -> Name {
match v {
None => Name::Anonymous,
Some(n) => Name::Named { name: n.clone() },
}
}
}

View File

@ -1,66 +0,0 @@
use crate::actor::*;
use std::fmt::Debug;
use std::io;
use std::sync::Arc;
struct Tracer(tracing::Span);
fn set_name_oid<M>(t: &mut Tracer, r: &Arc<Ref<M>>) {
t.0.record("oid", &tracing::field::display(&r.oid()));
}
pub fn tracer<M: Debug>(t: &mut Activation, name: tracing::Span) -> Arc<Ref<M>> {
let mut e = Tracer(name);
let r = t.create_inert();
set_name_oid(&mut e, &r);
r.become_entity(e);
r
}
impl<M: Debug> Entity<M> for Tracer {
fn assert(&mut self, _t: &mut Activation, a: M, h: Handle) -> ActorResult {
let _guard = self.0.enter();
tracing::trace!(?a, ?h, "assert");
Ok(())
}
fn retract(&mut self, _t: &mut Activation, h: Handle) -> ActorResult {
let _guard = self.0.enter();
tracing::trace!(?h, "retract");
Ok(())
}
fn message(&mut self, _t: &mut Activation, m: M) -> ActorResult {
let _guard = self.0.enter();
tracing::trace!(?m, "message");
Ok(())
}
fn sync(&mut self, t: &mut Activation, peer: Arc<Ref<Synced>>) -> ActorResult {
let _guard = self.0.enter();
tracing::trace!(?peer, "sync");
t.message(&peer, Synced);
Ok(())
}
}
/// Sets up [`tracing`] logging in a reasonable way.
///
/// Useful at the top of `main` functions.
pub fn convenient_logging() -> Result<(), Box<dyn std::error::Error>> {
let filter = match std::env::var(tracing_subscriber::filter::EnvFilter::DEFAULT_ENV) {
Err(std::env::VarError::NotPresent) =>
tracing_subscriber::filter::EnvFilter::default()
.add_directive(tracing_subscriber::filter::LevelFilter::INFO.into()),
_ =>
tracing_subscriber::filter::EnvFilter::try_from_default_env()?,
};
let subscriber = tracing_subscriber::fmt()
.with_ansi(true)
.with_thread_ids(true)
.with_max_level(tracing::Level::TRACE)
.with_env_filter(filter)
.with_writer(io::stderr)
.finish();
tracing::subscriber::set_global_default(subscriber)
.expect("Could not set tracing global subscriber");
Ok(())
}