{"id":77697,"date":"2019-08-13T09:00:16","date_gmt":"2019-08-13T16:00:16","guid":{"rendered":""},"modified":"2025-06-27T05:09:53","modified_gmt":"2025-06-27T12:09:53","slug":"trill-103-ingress-egress-trills-notion-of-time-tutorial","status":"publish","type":"post","link":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/","title":{"rendered":"Trill 103: Ingress, Egress, and Trill\u2019s notion of time"},"content":{"rendered":"\n<p>Congratulations! You\u2019ve made it to the next installment of our overview of Trill, Microsoft\u2019s open source streaming data engine. As noted in our previous posts about <a href=\"https:\/\/cloudblogs.microsoft.com\/opensource\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/\">basic queries<\/a> and <a href=\"https:\/\/cloudblogs.microsoft.com\/opensource\/2019\/05\/01\/trill-102-temporal-joins\/\">joins<\/a>, Trill is a <em>temporal query processor<\/em>. Trill works with data that has some intrinsic notion of time. However, Trill doesn\u2019t assign any semantics to that notion of time other than it is some value that is always generally increasing. So, what could qualify as \u201ctime\u201d to Trill? It could be just about anything:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Event time, or a time that is naturally associated with each event, such as the time a sensor reading is taken<\/li>\n\n\n\n<li>System time, or the time when an event arrives at the server or event queue<\/li>\n\n\n\n<li>Processing time, or the time when an event arrives at the given processing node running Trill<\/li>\n\n\n\n<li>Incremental time, or just a value that increments for each event seen<\/li>\n<\/ul>\n\n\n\n<p>No matter where your \u201ctime\u201d concept comes from, Trill will work for it. All Trill needs is for it to be a long-valued field and Trill is ready to go.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"tell-me-what-to-do\">Tell me what to do<\/h2>\n\n\n\n<p>There are several ways to tell Trill what the time component of the data is. The most explicit way to do it is using StreamEvent objects that attach to every event a lifespan. StreamEvent objects can be created using static factory methods on the StreamEvent class. For instance, this method creates an event with a set interval:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_1.png\" alt=\"Set interval code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>This method creates an event called a \u201cstart edge\u201d, which is in effect an interval with no set end time:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_2.png\" alt=\"End time code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Lastly, if the data really corresponds to just point events, there is a method for that as well, though inside Trill a point is simply stored as an interval of minimal length 1 tick:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_3.png\" alt=\"Point code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>If you have an observable of these <code>StreamEvent<\/code> objects, getting data into Trill is straightforward using the method <code>ToStreamable<\/code>:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_4.png\" alt=\"Observable code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"or-just-give-me-a-hint\">Or just give me a hint<\/h2>\n\n\n\n<p>If you don\u2019t want to go through the extra step of creating <code>StreamEvent<\/code> objects, there are a few ways to bring data directly into Trill while also telling it how to reason about time. How you do it depends on if your data already has a time component, or otherwise if you want Trill to assign time to your data. Let\u2019s look at one example, given just an observable of data values:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_5.png\" alt=\"Observable code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Breaking this example down, note first that the result of the method call is the same IStreamable type as with the <code>StreamEvent<\/code> example. The only difference is that instead of assigning time in the observable, we are assigning it in the ToStreamable call. The method name has changed to ToTemporalStreamable because the input data has a natural time component called \u201cTime\u201d. This method is the logical equivalent of wrapping each element in the observable with a <code>StreamEvent.CreateStart<\/code> call beforehand, but without allocating the <code>StreamEvent<\/code> objects.<\/p>\n\n\n\n<p>What if instead of a single time field in TPayload there are two temporal fields, indicating start and end time? There is an overload for that as well, with lambdas for identifying the start and end fields:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_6.png\" alt=\"TPayload code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>What if type TPayload doesn\u2019t have a natural time component? In that case, Trill itself can assign time values to events depending on user needs. The following example assigns to each event the current system time at the point of ingress:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_7.png\" alt=\"Set interval code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>This last example is a clever way to produce <em>progressive<\/em> results from an arbitrary data stream. Instead of assigning to each event a \u201ctime\u201d that corresponds to something like what we would see on a clock, we assign a sequence number that bumps every 100 input rows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_8.png\" alt=\"Observable code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>This example uses Trill\u2019s native temporal capabilities to produce partial results from what someone would normally think of as \u201catemporal\u201d data. Want to calculate the average value of a sequence of integers, but want to get a partial result of said average every 100 values? Assign the first 100 values a time of \u201c0\u201d, the next 100 values a time of \u201c1\u201d, and so on, and Trill will do all the rest for you:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_9.png\" alt=\"code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>The final line of the example above shows how to get data out of Trill and back into an observable. Perhaps ironically and not surprisingly, we will cover this topic briefly at the end of this post.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"order-order\">Order! Order!<\/h2>\n\n\n\n<p>While the section above demonstrates how to assign time to events in Trill, there is one more wrinkle that must be dealt with and must be done at ingress. Trill\u2019s query engine is an <strong>\u00ad<em>in-order<\/em> data processor<\/strong>. It is incredibly efficient in memory usage with high throughput, but many of the tricks it uses internally rely on data being presorted, such as the following:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Trill-103_1-1024x298.png\" alt=\"\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>If data resides in a database and is sorted and indexed for historical queries, there is no problem with the sorting assumption. However, in this strange setting called the \u201creal world,\u201d data does not always arrive at the query processor in the desired order, especially in streaming as opposed to offline settings. Hence, the input data may look a little more like this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Trill-103_2-1024x305.png\" alt=\"Real world data example\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Trill\u2019s paradigm for dealing with data that arrives out of order is to attend to it at ingress. Any data that makes it past ingress must be ordered. One simple option is to throw an exception as soon as out-of-order data is found:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Trill-103_3-1024x316.png\" alt=\"Exception example\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>This solution is not exactly relevant for most online streaming scenarios. However, if your temporal data has been stored to disk and has been sorted by time (or at least should have been), it\u2019s a good safety check to have. It\u2019s also a good option if the time associated with each event is assigned at ingress time.<\/p>\n\n\n\n<p>Another option is to ignore any data that arrives out of order:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Trill-103_4-1024x306.png\" alt=\"Example of ignoring out of order data\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Lastly, one can adjust the timestamp of data that arrives out of order to match the most recent timestamp:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Trill-103_5-1024x309.png\" alt=\"\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>The author sets one of these <em>disorder policies<\/em> at the point of ingress by adding additional parameters to the ingress methods, like this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_10.png\" alt=\"disorder policies code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Dropped data and pre-adjusted data are both sent to a separate stream that can be monitored:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_11.png\" alt=\"dropped events code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"patience-is-a-virtue\">Patience is a virtue<\/h2>\n\n\n\n<p>The three options presented in the previous section work on their own for some scenarios but leave many scenarios unsupported. Far more often than not, the prospect of dropping data or adjusting timestamps is, put lightly, a major no-no. To cover more cases of disordered real-world streaming data, Trill offers two additional features that address common patterns.<\/p>\n\n\n\n<p>Firstly, Trill allows the query author to specify a <em>lag allowance,<\/em> a fixed period that Trill buffers incoming data and sorts it on timestamp. Think of the lag allowance as a traveling window of time within which data is held back and sorted. Trill uses a <a href=\"https:\/\/dl.acm.org\/citation.cfm?doid=2588555.2593662\">sorting algorithm<\/a> that is very fast in most cases but is especially fast in scenarios where the data is \u201calmost sorted\u201d, which is a great way to characterize incoming stream data. Namely, in nearly all real-world cases, as streaming data arrives the high watermark for time mostly moves forward. There are fluctuations and bits of disorder, yes, but generally speaking, time moves in one direction as opposed to data arriving at random times.<\/p>\n\n\n\n<p>Consider again the example of ingress data from the previous section, but this time with a lag allowance of time L:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Trill-103_6-1024x303.png\" alt=\"lag allowance example\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>When the first event arrives, it is not immediately processed. Instead, it is held in a buffer, and a window of time L is established. When the second event arrives, it is more than time L ahead of the first point. The first event leaves the buffer and gets processed, while the second event now gets buffered and sets a new window.<\/p>\n\n\n\n<p>Now, the first out-of-order point arrives. It trails in time from the second point, but it is within the sorting window. It is added to the buffer, timestamp unchanged, in sorted order:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Trill-103_7-1024x303.png\" alt=\"Event sorting example\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Allowing some lag offers the query author a latency\/correctness tradeoff; the longer the author is willing to wait for results, the more likely that disordered input data can be sorted inline without any adjustment. For a larger lag allowance, it is possible to include the remaining two out-of-order points; however, the user will need to wait longer for results.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_12.png\" alt=\"Set interval code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"going-their-separate-ways\">Going their separate ways<\/h2>\n\n\n\n<p>Trill\u2019s second trick up its sleeve for handling disordered input is especially suited for an Internet-of-Things (IoT) environment. A common scenario in IoT settings is for the clocks on each device to not be entirely in sync. Devices may join and leave the network, either purposefully or from network partitioning, at any time. Skew between device times is not only common but expected.<\/p>\n\n\n\n<p>Also common in IoT settings are queries that are run <em>per device<\/em> as opposed to over the entire network. If the user wants, for instance, to know the maximum temperature reading per hour per device, we absolutely do not want any data from one sensor to be adjusted relative to another. If anything, <em>we want each device in the network to essentially have its own timeline<\/em> independent of one another:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Trill-103_8-1024x303.png\" alt=\"IoT example\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>In the example above, the data does not arrive in a single global order. However, notice that the data <em>does<\/em> arrive in temporal order <em>for each device<\/em>.<\/p>\n\n\n\n<p>Trill allows this scenario using a feature called <em>partitioned streams<\/em>. Each partition is allowed its own timeline that has its own high watermark. The disorder policies are only applied <em>per device<\/em> if needed at all. Then Trill can compute the query over each device without needing a global order.<\/p>\n\n\n\n<p>There are two ways to enable this feature in Trill. The more explicit way to do so is to create PartitionedStreamEvent objects instead of StreamEvent objects:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_13.png\" alt=\"Stream event code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>The second way is to use the ToPartitionedStreamable methods, which are identical to ToTemporalStreamable methods mentioned above but with an additional method describing how to extract the partition information from the input data:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_14.png\" alt=\"partitioned stream code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"parting-is-such-sweet-sorrow\">Parting is such sweet sorrow<\/h2>\n\n\n\n<p>So now data has been brought into Trill. Then using guidance from our other blog posts has been processed using your amazing query. It\u2019s now time to get data back out of Trill and into an observable. Without having to worry about ordering, egressing data is a much simpler prospect. We already saw an example of egress in an earlier example:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_15.png\" alt=\"egress code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Let\u2019s break this example down:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The ToTemporalObservable method says that we want to create an observable output, and that we may want to peek at the temporal information when we do.<\/li>\n<\/ul>\n\n\n\n<p>The lambda expression parameter in bold describes how to construct results for the observable. In this case we are dropping the time information because the example was about giving partial results of an average and the time component is less relevant, but we could have just as easily done something like this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_16.png\" alt=\"Set interval code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Alternatively, we can get results in the same StreamEvent form that we could use at ingress:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Code-snip_17.png\" alt=\"stream event code snippet image\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>These two egress methods cover nearly all of the cases that most people will encounter when using Trill, but if you\u2019d like to learn more about the more complicated egress methods, <a href=\"mailto:asktrill@microsoft.com?subject=Egress%20methods\">drop us a line<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"class-is-almost-dismissed\">Class is almost dismissed<\/h2>\n\n\n\n<p>We are always interested in identifying common patterns of time disorder that can be resolved at ingress. For instance, one pattern we have noticed but not yet implemented is <a href=\"https:\/\/github.com\/microsoft\/Trill\/issues\/14\">future-outliers<\/a>:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Trill-103_9-1024x297.png\" alt=\"future outliers example\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>A data point may arrive seemingly so far out of order that it must be an error. For instance, what if a data point arrives that claims to be three years beyond the most recently seen timestamp? Such a situation could be caused by a network partition closing, or a new device coming online with a defective clock, or any number of other reasons. This pattern, among others, could be recognized at ingress time and dealt with in a user-defined way. Identifying and defining these patterns is a small task that is an excellent entry point for anyone who is interested in contributing to Trill.<\/p>\n\n\n\n<p>In the meantime, our code can be found <a href=\"https:\/\/github.com\/Microsoft\/Trill\">here<\/a>, and our amazing <a href=\"https:\/\/github.com\/Microsoft\/TrillSamples\">samples<\/a> can give you a good head start on programming over Trill. <a href=\"mailto:asktrill@microsoft.com\">Reach out to us anytime<\/a> if there\u2019s something we can do to help you on your way. We hope to see you next time with our last entry in the Introduction to Trill blog series when we deep-dive into aggregation and pattern matching, possibly the most powerful feature Trill offers.\u00a0<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Congratulations! You\u2019ve made it to the next installment of our overview of Trill, Microsoft\u2019s open source streaming data engine. As noted in our previous posts about basic queries and joins, Trill is a temporal query processor. Trill works with data that has some intrinsic notion of time.<\/p>\n","protected":false},"author":5562,"featured_media":95486,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"msxcm_post_with_no_image":false,"ep_exclude_from_search":false,"_classifai_error":"","_classifai_text_to_speech_error":"","footnotes":""},"post_tag":[2272],"content-type":[340],"topic":[2238,2239,2241],"programming-languages":[2253],"coauthors":[424],"class_list":["post-77697","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-microsoft","content-type-tutorials-and-demos","topic-ai-machine-learning","topic-analytics","topic-cloud","programming-languages-net","review-flag-1593580428-734","review-flag-1-1593580432-963","review-flag-alway-1593580310-39","review-flag-iot-1680213327-385","review-flag-micro-1680215167-604","review-flag-new-1593580248-669"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Trill 103: Ingress, Egress, and Trill\u2019s notion of time | Microsoft Open Source Blog<\/title>\n<meta name=\"description\" content=\"This is the next installment of our overview of Trill, Microsoft\u2019s open source streaming data engine that&#039;s designed to process one trillion events a day.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Trill 103: Ingress, Egress, and Trill\u2019s notion of time | Microsoft Open Source Blog\" \/>\n<meta property=\"og:description\" content=\"This is the next installment of our overview of Trill, Microsoft\u2019s open source streaming data engine that&#039;s designed to process one trillion events a day.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/\" \/>\n<meta property=\"og:site_name\" content=\"Microsoft Open Source Blog\" \/>\n<meta property=\"article:published_time\" content=\"2019-08-13T16:00:16+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-27T12:09:53+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/STB13_Jaden_06.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1170\" \/>\n\t<meta property=\"og:image:height\" content=\"640\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"James Terwilliger\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@OpenAtMicrosoft\" \/>\n<meta name=\"twitter:site\" content=\"@OpenAtMicrosoft\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"James Terwilliger\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 min read\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/\"},\"author\":[{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/author\/james-terwilliger\/\",\"@type\":\"Person\",\"@name\":\"James Terwilliger\"}],\"headline\":\"Trill 103: Ingress, Egress, and Trill\u2019s notion of time\",\"datePublished\":\"2019-08-13T16:00:16+00:00\",\"dateModified\":\"2025-06-27T12:09:53+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/\"},\"wordCount\":2062,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/STB13_Jaden_06.webp\",\"keywords\":[\"Microsoft\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/\",\"name\":\"Trill 103: Ingress, Egress, and Trill\u2019s notion of time | Microsoft Open Source Blog\",\"isPartOf\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/STB13_Jaden_06.webp\",\"datePublished\":\"2019-08-13T16:00:16+00:00\",\"dateModified\":\"2025-06-27T12:09:53+00:00\",\"description\":\"This is the next installment of our overview of Trill, Microsoft\u2019s open source streaming data engine that's designed to process one trillion events a day.\",\"breadcrumb\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#primaryimage\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/STB13_Jaden_06.webp\",\"contentUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/STB13_Jaden_06.webp\",\"width\":1170,\"height\":640},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/opensource.microsoft.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Trill 103: Ingress, Egress, and Trill\u2019s notion of time\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#website\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/\",\"name\":\"Microsoft Open Source Blog\",\"description\":\"Open dialogue about openness at Microsoft \u2013 open source, standards, interoperability\",\"publisher\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/opensource.microsoft.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#organization\",\"name\":\"Microsoft Open Source Blog\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png\",\"contentUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png\",\"width\":259,\"height\":194,\"caption\":\"Microsoft Open Source Blog\"},\"image\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/OpenAtMicrosoft\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Trill 103: Ingress, Egress, and Trill\u2019s notion of time | Microsoft Open Source Blog","description":"This is the next installment of our overview of Trill, Microsoft\u2019s open source streaming data engine that's designed to process one trillion events a day.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/","og_locale":"en_US","og_type":"article","og_title":"Trill 103: Ingress, Egress, and Trill\u2019s notion of time | Microsoft Open Source Blog","og_description":"This is the next installment of our overview of Trill, Microsoft\u2019s open source streaming data engine that's designed to process one trillion events a day.","og_url":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/","og_site_name":"Microsoft Open Source Blog","article_published_time":"2019-08-13T16:00:16+00:00","article_modified_time":"2025-06-27T12:09:53+00:00","og_image":[{"width":1170,"height":640,"url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/STB13_Jaden_06.png","type":"image\/png"}],"author":"James Terwilliger","twitter_card":"summary_large_image","twitter_creator":"@OpenAtMicrosoft","twitter_site":"@OpenAtMicrosoft","twitter_misc":{"Written by":"James Terwilliger","Est. reading time":"8 min read"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#article","isPartOf":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/"},"author":[{"@id":"https:\/\/opensource.microsoft.com\/blog\/author\/james-terwilliger\/","@type":"Person","@name":"James Terwilliger"}],"headline":"Trill 103: Ingress, Egress, and Trill\u2019s notion of time","datePublished":"2019-08-13T16:00:16+00:00","dateModified":"2025-06-27T12:09:53+00:00","mainEntityOfPage":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/"},"wordCount":2062,"commentCount":0,"publisher":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#organization"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#primaryimage"},"thumbnailUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/STB13_Jaden_06.webp","keywords":["Microsoft"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/","url":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/","name":"Trill 103: Ingress, Egress, and Trill\u2019s notion of time | Microsoft Open Source Blog","isPartOf":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#primaryimage"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#primaryimage"},"thumbnailUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/STB13_Jaden_06.webp","datePublished":"2019-08-13T16:00:16+00:00","dateModified":"2025-06-27T12:09:53+00:00","description":"This is the next installment of our overview of Trill, Microsoft\u2019s open source streaming data engine that's designed to process one trillion events a day.","breadcrumb":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#primaryimage","url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/STB13_Jaden_06.webp","contentUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/STB13_Jaden_06.webp","width":1170,"height":640},{"@type":"BreadcrumbList","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/opensource.microsoft.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Trill 103: Ingress, Egress, and Trill\u2019s notion of time"}]},{"@type":"WebSite","@id":"https:\/\/opensource.microsoft.com\/blog\/#website","url":"https:\/\/opensource.microsoft.com\/blog\/","name":"Microsoft Open Source Blog","description":"Open dialogue about openness at Microsoft \u2013 open source, standards, interoperability","publisher":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/opensource.microsoft.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/opensource.microsoft.com\/blog\/#organization","name":"Microsoft Open Source Blog","url":"https:\/\/opensource.microsoft.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png","contentUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png","width":259,"height":194,"caption":"Microsoft Open Source Blog"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/OpenAtMicrosoft"]}]}},"msxcm_display_generated_audio":false,"msxcm_animated_featured_image":null,"distributor_meta":false,"distributor_terms":false,"distributor_media":false,"distributor_original_site_name":"Microsoft Open Source Blog","distributor_original_site_url":"https:\/\/opensource.microsoft.com\/blog","push-errors":false,"_links":{"self":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/77697","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/users\/5562"}],"replies":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/comments?post=77697"}],"version-history":[{"count":1,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/77697\/revisions"}],"predecessor-version":[{"id":97734,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/77697\/revisions\/97734"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/media\/95486"}],"wp:attachment":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/media?parent=77697"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/post_tag?post=77697"},{"taxonomy":"content-type","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/content-type?post=77697"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/topic?post=77697"},{"taxonomy":"programming-languages","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/programming-languages?post=77697"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/coauthors?post=77697"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}