diff --git a/README.md b/README.md index 8da7112..f39b352 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ -FsShelter [![Windows Build](https://ci.appveyor.com/api/projects/status/c0oom3oyr8qnrsc8?svg=true)](https://ci.appveyor.com/project/et1975/fsshelter) - [![Mono/OSX build](https://travis-ci.org/Prolucid/FsShelter.svg?branch=master)](https://travis-ci.org/Prolucid/FsShelter) +FsShelter [![Windows Build](https://ci.appveyor.com/api/projects/status/c0oom3oyr8qnrsc8?svg=true)](https://ci.appveyor.com/project/et1975/fsshelter) [![Mono/OSX build](https://travis-ci.org/Prolucid/FsShelter.svg?branch=master)](https://travis-ci.org/Prolucid/FsShelter) ======= A library for defining and running Apache Storm topologies in F# using statically typed streams. Based on and a complete rewrite of [FsStorm](https://github.com/FsStorm) with the goals of static typing, modularity and pluggable serialization. -Comes bundled with Json serialization, Thrift and Protobuf (Thrift and Protobuf require corresponding Storm multilang serializer implementations [Thriftshell](https://github.com/prolucid/thriftshell) or [Protoshell](https://github.com/prolucid/protoshell)). +Comes bundled with Json serialization, Thrift and Protobuf (Thrift and Protobuf require corresponding Storm multilang serializer implementation [Thriftshell](https://github.com/prolucid/thriftshell) or [Protoshell](https://github.com/prolucid/protoshell)). See [docs][docs] for for an intro and an overview. @@ -13,7 +12,7 @@ Join the conversation: [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](TBD # Limitations * At the moment FsShelter doesn't support direct emits. -* [STORM-1644](https://issues.apache.org/jira/browse/STORM-1644): Currently, when running on Windows, the process will run under cmd.exe incurring slight memory overhead. +* [STORM-1644](https://issues.apache.org/jira/browse/STORM-1644): Currently, when running on Windows, the process will run under cmd.exe incurring slight overhead. # Building On Windows, from the cloned root: @@ -33,11 +32,11 @@ IDE: Install NUnit plugin for VS or MonoDevelop to see the unit-tests in Test Ex # Submitting the topology Have a local [Storm](https://storm.apache.org/downloads.html) installed and running. ``` -samples\Simple\bin\Release\Simple submit-local +samples\WordCount\bin\Release\WordCount submit-local ``` or, if running on Mono: ``` -mono samples/Simple/bin/Release/Simple.exe submit-local +mono samples/WordCount/bin/Release/WordCount.exe submit-local ``` # Seeing the topology in action diff --git a/build.fsx b/build.fsx index 9fe4def..765e5c8 100644 --- a/build.fsx +++ b/build.fsx @@ -370,6 +370,24 @@ Target "WordCountSvg" (fun _ -> |> ignore ) +Target "GuaranteedSvg" (fun _ -> + Shell.Exec( +#if MONO + "mono", + ""+ +#else + Environment.GetEnvironmentVariable("ComSpec"), + "/c "+ +#endif + ("samples" @@ "Guaranteed" @@ "bin" @@ "Release" @@ "Guaranteed.exe") + + " graph | dot -Tsvg -o " + build_out + "/Guaranteed.svg") + |> ignore +) + +Target "ExportGraphs" DoNothing +"ExportGraphs" + <== ["Build";"WordCountSvg";"GuaranteedSvg"] + // -------------------------------------------------------------------------------------- // Run all targets by default. Invoke 'build ' to override @@ -388,7 +406,6 @@ Target "All" DoNothing "All" #if MONO #else - ==> "GenerateSources" =?> ("SourceLink", Pdbstr.tryFind().IsSome ) #endif ==> "NuGet" diff --git a/docs/content/custom.fsx b/docs/content/custom.fsx deleted file mode 100644 index 29eb09a..0000000 --- a/docs/content/custom.fsx +++ /dev/null @@ -1,86 +0,0 @@ -(*** hide ***) -// This block of code is omitted in the generated HTML documentation. Use -// it to define helpers that you do not want to show in the documentation. -#I "../../bin" -#r "FsShelter.dll" - -(** -Implementing custom reliability semantics -======================== -The reliable spout implementation for a custom source, like a queue (RabbitMQ, Kafka, etc) needs to obtain the event id from the source and forward Storm's acks and nacks to the source, which could be accomplished in FsShelter with: - - a housekeeper -*) - -open FsShelter - -/// Maintains reliability semantics of a queueSpout. -/// ack: thread-safe method to ack a message by id. -/// nack: thread-safe method to nack a message by id. -let createQueueHousekeeper ack nack = - fun (msg:Json) -> - match msg?command.Val with - | "ack" -> ack (int64 (msgId msg)) - | "fail" -> nack (int64 (msgId msg)) - | _ -> () - -(** - and custom runner -*) - -/// Partially configures a reliable runner for the queues. -/// ack: thread-safe method to ack a message by id. -/// nack: thread-safe method to nack a message by id. -let createQueueRunner ack nack = Storm.reliableSpoutRunner - (createQueueHousekeeper (uint64 >> ack) (uint64 >> nack)) - - -(** - then given an event type - -*) -type Event<'m> = { msg:'m; id:uint64 } - -(** - we can declare a spout factory like this: - -*) - -/// Reads message from a queue into a tuple stream. -/// get: thread-safe event consumer. -/// toTuple: message object to Json tuple converter. -let createQueueSpout get toTuple emit = - fun () -> - async { - match get() with - | Some evt -> toTuple evt.msg |> emit (int64 evt.id) - | None -> do () - } - -(** - and a queue/consumer interface - -*) -/// this is the consumer interface into the queue -/// that returns three functions: get:unit->Event, ack:uint64->unit and nack:unit64->unit -let getConsumer cfg = - ((fun () -> { msg = 1b; id = 0ul }), //get the next message and its id - (fun id -> ()), // forward the ack to the queue - (fun id -> ())) // forward the nack to the queue - -(** - we can implement the actual spout like this: - -*) - -let statusEvents runner getConsumer (cfg : Configuration) = - let toTuple (id:string,status:byte) = - tuple [ id ] - |> namedStream (match char status with | '1' -> "online" | _ -> "offline") - let get, ack, nack = getConsumer cfg - createQueueSpout get toTuple |> runner ack nack - -(** -Passing the createQueueRunner from the topology will tie all the pieces together and the ids, acks and nacks should start flowing from the source and back. - -*) diff --git a/docs/content/guaranteed.fsx b/docs/content/guaranteed.fsx index da3afed..438a8e5 100644 --- a/docs/content/guaranteed.fsx +++ b/docs/content/guaranteed.fsx @@ -1,115 +1,136 @@ (*** hide ***) // This block of code is omitted in the generated HTML documentation. Use // it to define helpers that you do not want to show in the documentation. -#I "../../bin" +#I "../../build" #r "FsShelter.dll" -#r "FsJson.dll" -#r "FsLogging.dll" -open FsJson -open Storm +open FsShelter +open System +open System.Collections.Generic (** Defining reliable spouts -======================== +-------------------- [Processing guarantees](https://storm.apache.org/documentation/Guaranteeing-message-processing.html) are the biggest selling point of Storm, please see the official docs for the details. -FsShelter implements reliability semantics with "housekeeper" functions: defaultHousekeeper can be used as is for transient sources or as an inspiration for reliability over external/persistent sources. -The spout implementation is fairly similar to the "unreliable" version, with the addition of unique (int64) tuple ID: +The reliable spout implementation for a source like peristent a queue (RabbitMQ, Kafka, etc) needs to obtain the event id from the source and forward Storm's acks and nacks back to the source. +The obtained Id has to be passed along with the tuple from the spout function: +*) + +// data schema for the topology, every case is a unqiue stream +type Schema = + | Original of int + | Even of int + | Odd of int + +// numbers spout - produces messages +let numbers source = + async { + let! (tupleId,number) = source() + return Some(tupleId, Original (number)) + } + +// add 1 bolt - consumes and emits messages to either Even or Odd stream +let addOne (input,emit) = + async { + match input with + | Original x -> + match x % 2 with + | 1 -> Even (x+1) + | _ -> Odd (x+1) + | _ -> failwithf "unexpected input: %A" input + |> emit + } + +// terminating bolt - consumes messages +let logResult (info,input) = + async { + match input with + | Even x + | Odd x -> info (sprintf "Got: %A" input) + | _ -> failwithf "unexpected input: %A" input + } +(** +Here we mimic an external source and implement all three possible cases: produce a new message, retry a failed one (indefinetely) and ack a successfully processed. *) -let rnd = new System.Random() // used for generating random messages +open FsShelter.Topology + +type QueueCmd = + | Get of AsyncReplyChannel + | Ack of TupleId + | Nack of TupleId -///cfg: the configution passed in by storm -///runner: a spout runner function (passed in from topology) -let spout runner (cfg:Configuration) = +// faking an external source here +let source = + let rnd = Random() let count = ref 0L - //define the "next" function - //emit: a function that emits message to storm with unique ID - let next emit = - fun () -> async { - tuple [ rnd.Next(0, 100) ] - |> emit (Threading.Interlocked.Increment &count.contents) - } - //run the spout - next |> runner + let pending = Dictionary() + + MailboxProcessor.Start (fun inbox -> + let rec loop nacked = + async { + let! cmd = inbox.Receive() + return! loop <| match cmd, nacked with + | Get rc, [] -> + let tupleId,number = string(Threading.Interlocked.Increment &count.contents), rnd.Next(0, 100) + pending.Add(tupleId,number) + rc.Reply(tupleId,number) + [] + | Get rc,(tupleId,number)::xs -> + pending.Add(tupleId,number) + rc.Reply (tupleId,number) + xs + | Ack id, _ -> + pending.Remove id |> ignore + nacked + | Nack id, _ -> + (id,pending.[id])::nacked + } + loop []) + (** -Anchoring and named streams -======================== - -FsShelter has helper functions to emit to a named stream or to anchor a tuple: +Anchoring +-------------------- +In order to provide processing guarantees Storm needs to construct and track the state of entire "tuple tree", which is built out by emitting "anchored" tuples. +FsShelter implements anchoring statically: instead of ad-hoc, as determined by a component, it is a property of the stream _leading to_ an emit. +Consequently the implementation of emit (anchored/unanchored) is determined by the topology graph and completely transparent to the bolt that processes a tuple that will be used as an anchor. *) -///cfg: the configuration passed in from Storm -///runner: passed in from topology -///emit: passed in from topology -let addOneBolt runner emit cfg = - //define the consumer function - let add (msg : Json) = - async { - let x = msg?tuple.[0].ValI + 1 - tuple [ x ] - // write to a named stream - |> namedStream (match x % 2 with | 0 -> "even" | _ -> "odd") - // anchor to ensure the entire tuple tree is processed before the spout is ack'ed - |> anchor msg // anchor to the original message - |> emit - } - //run the bolt - add |> runner -(** -Example of parametrization and use of the tuple's origin (component that emitted it and the stream that it arrived on) inspection: +//define the storm topology +open FsShelter.DSL -*) -///cfg: the configuration passed in by Storm -///runner: passed in from topology -///log: log write -let resultBolt runner log (cfg:Configuration) = - let desc = cfg.Json?conf?desc.Val // the value passed in with the submitted topology Config - //define the function that will return the consumer - let logResult (msg : Json) = - async { - log desc (sprintf "origin: %A(%A), data: %A" msg?comp.Val msg?stream.Val msg?tuple.[0].ValI) - } - //run the bolt - logResult |> runner +#nowarn "25" // for stream matching expressions +let sampleTopology = topology "Guaranteed" { + let s1 = numbers + |> runReliableSpout (fun log cfg () -> source.PostAndAsyncReply Get) // ignoring logging and cfg available + (fun _ -> Ack >> source.Post, Nack >> source.Post) + let b1 = addOne + |> runBolt (fun log cfg tuple emit -> (tuple,emit)) // pass incoming tuple and emit function + |> withParallelism 2 + + let b2 = logResult + |> runBolt (fun log cfg -> + let mylog = Common.Logging.asyncLog ("odd.log") + fun tuple emit -> (mylog,tuple)) + |> withParallelism 1 -(** -Topology with named streams and config overrides -======================== + let b3 = logResult + |> runBolt (fun log cfg -> + let mylog = Common.Logging.asyncLog ("even.log") + fun tuple emit -> (mylog,tuple)) + |> withParallelism 1 -*) + yield s1 ==> b1 |> shuffle.on Original // emit from s1 to b1 on Original stream and anchor immediately following emits to this tuple + yield b1 --> b2 |> shuffle.on Odd // anchored emit from b1 to b2 on Odd stream + yield b1 --> b3 |> shuffle.on Even // anchored emit from b1 to b2 on Even stream +} + +(** +Resulting topology graph: -open StormDSL - -let topology = - { TopologyName = "FstGuaranteed" - Spouts = - [ { Id = "ReliableSpout" - Outputs = [ Default [ "number" ] ] - Spout = Local { Func = spout (Storm.reliableSpoutRunner Storm.defaultHousekeeper) } - Config = jval [ "topology.max.spout.pending", jval 123 ] // override "backpressure" - Parallelism = 1 } ] - Bolts = - [ { Id = "AddOneBolt" - Outputs = [ Named("even", [ "number" ]) // named stream "even" - Named("odd", [ "number" ]) ] - Inputs = [ DefaultStream "ReliableSpout", Shuffle ] - Bolt = Local { Func = addOneBolt Storm.autoAckBoltRunner Storm.emit } - Config = JsonNull - Parallelism = 2 } - { Id = "EvenResultBolt" - Outputs = [] - Inputs = [ Stream("AddOneBolt","even"), Shuffle ] - Bolt = Local { Func = resultBolt Storm.autoAckBoltRunner } - Config = jval ["desc", "even"] // pass custom config property to the component - Parallelism = 1 } - { Id = "OddResultBolt" - Outputs = [] - Inputs = [ Stream("AddOneBolt","odd"), Shuffle ] - // logs to custom (FsLogging) pid-based log file - Bolt = Local { Func = resultBolt Storm.autoAckBoltRunner Logging.log } - Config = jval ["desc", "odd"] // pass custom config property to the component - Parallelism = 1 } ] } +![SVG](svg/Guaranteed.svg "Guaranteed (SVG)") +The solid lines represent "anchoring" streams and the dotted lines indicate the outer limits of the processing guarantees: a tuple emitted along a dotted line is only anchored if the line leading to it is solid. +*) diff --git a/docs/content/index.fsx b/docs/content/index.fsx index 5c0ee35..b179845 100644 --- a/docs/content/index.fsx +++ b/docs/content/index.fsx @@ -11,8 +11,9 @@ FsShelter Overview ------- -FsShelter is a library for implementation of [Apache Storm](https://storm.apache.org/) components, definition of topologies in F# DSL and submission via embedded Thrift client for execution. -It is based on and a major rewrite of [FsStorm](https://github.com/FsStorm). It departs from FsStrom in significant ways and therefore has been split off into itsown project. +FsShelter is a library for implementation of [Apache Storm](https://storm.apache.org/) components and definition of topologies in F# DSL. +The Management module also provides wrappers for Nimbus Thrift API, allowing to bundle and submit a topology for execution. +FsShelter is based on and a major rewrite of [FsStorm](https://github.com/FsStorm). It departs from FsStrom in significant ways and therefore has been split off into itsown project. The topology and the components could be implemented in a single EXE project and are executed by Storm via its [multilang](https://storm.apache.org/documentation/Multilang-protocol.html) protocol as separate processes - one for each task/instance. Corresponding [ProtoShell](https://github.com/prolucid/protoshell) and [ThriftShell](https://github.com/prolucid/thriftshell) libraries facilitate Protobuf and Thrift serialization, which improve throughput of FsShelter components as compared to standard JSON. @@ -138,15 +139,13 @@ sampleTopology |> DotGraph.writeToConsole Samples & documentation ----------------------- - * [Simple](simple.html) contains a "unreliable" spout example - emitted tuples do not require ack, could be lost in case of failure. + * [WordCount](wordcount.html) contains a "unreliable" spout example - emitted tuples do not require ack, could be lost in case of failure. * [Guaranteed](guaranteed.html) contains a "reliable" spout example - emitted tuples have unique ID and require ack. * [API Reference](reference/index.html) contains automatically generated documentation for public types, modules and functions in the library. - * [WordCount](https://github.com/FsShelter/FsShelter.WordCount) contains a simple example showing a spout with two bolts. - Getting FsShelter ---------------- diff --git a/docs/content/wordcount.fsx b/docs/content/wordcount.fsx index b07d964..8d22f67 100644 --- a/docs/content/wordcount.fsx +++ b/docs/content/wordcount.fsx @@ -6,7 +6,7 @@ open System (** Defining the schema -======================== +-------------------- FsShelter uses F# discriminated unions to statically type streams: @@ -20,7 +20,7 @@ type Schema = (** Defining unreliable spouts -======================== +-------------------- FsShelter spouts can be implemented as "reliable" or "unreliable". Either implementation is a single function, returning async Option, where None indicates there's nothing to emit from this spout at this moment: @@ -32,7 +32,7 @@ let sentences source = async { return source() |> Sentence |> Some } (** Defining bolts -======================== +-------------------- Example of a FsShelter bolt that reads a tuple and emits another one: @@ -73,7 +73,7 @@ let logResult (log, input) = (** -And given these helper methods: +We will pass these implementations into the component funcions when we put things together in a topology definition. *) let source = @@ -108,13 +108,11 @@ let increment = (** -We are ready to define the topology. - Using F# DSL to define the topology -======================== +-------------------- -Topologies are defined using an embedded DSL: +Storm topology is a graph of spouts and bolts connected via streams. FsShelter provides an embedded DSL for defining the topologies, which allows mix and match of native java, external shell and FsShell components: *) open FsShelter.DSL @@ -149,8 +147,8 @@ let sampleTopology = } (** -Here we define a graph by declaring the components and connecting them with arrows. -The lambdas following the "run" methods privdes the opportunity to carry out construction of the arguments that will be passed into the component functions, where: +Here we define the graph by declaring the components and connecting them with arrows. +The lambda arguments for the "run" methods privde the opportunity to carry out construction of the arguments that will be passed into the component functions, where: * log is the Storm log factory * cfg is the runtime configuration passed in from Storm * tuple is the instance of the schema DU coming in @@ -158,6 +156,27 @@ The lambdas following the "run" methods privdes the opportunity to carry out con "log" and "cfg" are fixed once (curried) and as demonstrated in logBolt mkArgs lambda, one time-initialization can be carried out by inserting arbitrary code before "tuple" and "emit" arguments. This initialization will not be triggered unless the task execution is actually requsted by Storm for this specific instance of the process. + + +Exporting the topology graph +-------------- + +FsShelter includes a completely customizable GraphViz (dot) export functionality, here's what the word count topology looks like: + +![SVG](svg/WordCount.svg "WordCount (SVG)") + +The dotted lines represent "unanchored" streams and the number inside the `[]` shows the parallelism hint. +Which was achived by a simple export to console: +*) + +sampleTopology |> DotGraph.writeToConsole + +(** +Followed by a convertion into into SVG: + +```bash +mono samples/WordCount/bin/Release/WordCount graph | dot -Tsvg -o build/WordCount.svg +``` *) diff --git a/docs/files/svg/Guaranteed.svg b/docs/files/svg/Guaranteed.svg new file mode 100644 index 0000000..82a650e --- /dev/null +++ b/docs/files/svg/Guaranteed.svg @@ -0,0 +1,55 @@ + + + + + + +Guaranteed + + +s1 + +s1 +[1] + + +b1 + +b1 +[2] + + +s1->b1 + + +Original + + +b2 + +b2 +[1] + + +b1->b2 + + +Odd + + +b3 + +b3 +[1] + + +b1->b3 + + +Even + + + diff --git a/docs/files/svg/WordCount.svg b/docs/files/svg/WordCount.svg new file mode 100644 index 0000000..68cffca --- /dev/null +++ b/docs/files/svg/WordCount.svg @@ -0,0 +1,55 @@ + + + + + + +WordCount + + +sentencesSpout + +sentencesSpout +[1] + + +splitBolt + +splitBolt +[2] + + +sentencesSpout->splitBolt + + +Sentence + + +countBolt + +countBolt +[2] + + +logBolt + +logBolt +[2] + + +countBolt->logBolt + + +WordCount + + +splitBolt->countBolt + + +Word + + + diff --git a/docs/tools/templates/template.cshtml b/docs/tools/templates/template.cshtml index d44b515..3f832ae 100644 --- a/docs/tools/templates/template.cshtml +++ b/docs/tools/templates/template.cshtml @@ -45,20 +45,19 @@
  • Release Notes
  • Basics +
  • WordCount topology
  • Guaranteed processing
  • diff --git a/samples/Guaranteed/Topology.fs b/samples/Guaranteed/Topology.fs index be5e28a..414dafc 100644 --- a/samples/Guaranteed/Topology.fs +++ b/samples/Guaranteed/Topology.fs @@ -96,7 +96,7 @@ let sampleTopology = topology "Guaranteed" { fun tuple emit -> (mylog,tuple)) |> withParallelism 1 - yield s1 ==> b1 |> shuffle.on Original // emit from s1 to b1 on Original stream and anchor immediately following emits to this tree - yield b1 --> b2 |> shuffle.on Odd //(function Odd n -> n) // emit from b1 to b2 on Odd stream and group tuples by n - yield b1 --> b3 |> shuffle.on Even //(function Even n -> n) // emit from b1 to b2 on Even stream and group tuples by n + yield s1 ==> b1 |> shuffle.on Original // emit from s1 to b1 on Original stream and anchor immediately following emits to this tuple + yield b1 --> b2 |> shuffle.on Odd // anchored emit from b1 to b2 on Odd stream + yield b1 --> b3 |> shuffle.on Even // anchored emit from b1 to b2 on Even stream } diff --git a/src/FsShelter.sln b/src/FsShelter.sln index 6e3f123..43ed4f7 100644 --- a/src/FsShelter.sln +++ b/src/FsShelter.sln @@ -14,7 +14,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\paket.dependencies = ..\paket.dependencies ..\paket.lock = ..\paket.lock ..\paket.template = ..\paket.template - Performance1.psess = Performance1.psess ..\README.md = ..\README.md ..\RELEASE_NOTES.md = ..\RELEASE_NOTES.md EndProjectSection @@ -35,16 +34,12 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Common", "..\samples\Common EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{295576BD-0CFE-4D77-8468-C919E7446AB9}" ProjectSection(SolutionItems) = preProject - ..\docs\content\custom.fsx = ..\docs\content\custom.fsx ..\docs\content\guaranteed.fsx = ..\docs\content\guaranteed.fsx ..\docs\content\index.fsx = ..\docs\content\index.fsx ..\docs\content\wordcount.fsx = ..\docs\content\wordcount.fsx EndProjectSection EndProject Global - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU diff --git a/src/FsShelter/IO/JsonIO.fs b/src/FsShelter/IO/JsonIO.fs index c76ff8c..373416a 100644 --- a/src/FsShelter/IO/JsonIO.fs +++ b/src/FsShelter/IO/JsonIO.fs @@ -1,4 +1,5 @@ -module FsShelter.JsonIO +/// Json multilang IO +module FsShelter.JsonIO open Multilang open System @@ -9,9 +10,9 @@ open TupleSchema open Newtonsoft.Json.Linq [] -let END = "\nend\n" +let internal END = "\nend\n" -let toLog (msg:string) (lvl:int) = +let private toLog (msg:string) (lvl:int) = use sw = new StringWriter() use w = new JsonTextWriter(sw) w.WriteStartObject() diff --git a/src/FsShelter/IO/ProtoIO.fs b/src/FsShelter/IO/ProtoIO.fs index 99196cb..c0cf8bd 100644 --- a/src/FsShelter/IO/ProtoIO.fs +++ b/src/FsShelter/IO/ProtoIO.fs @@ -9,11 +9,11 @@ open Prolucid.ProtoShell open TupleSchema open System.IO -type V = Messages.Variant -type VL = WellKnownTypes.Value -type VLKind = VL.KindOneofCase -type VKind = V.KindOneofCase -let vNone = V(NoneVal = WellKnownTypes.NullValue.NULL_VALUE) +type internal V = Messages.Variant +type internal VL = WellKnownTypes.Value +type internal VLKind = VL.KindOneofCase +type internal VKind = V.KindOneofCase +let internal vNone = V(NoneVal = WellKnownTypes.NullValue.NULL_VALUE) let private typeMap = [ diff --git a/src/FsShelter/IO/ThriftIO.fs b/src/FsShelter/IO/ThriftIO.fs index 408a840..005e0cf 100644 --- a/src/FsShelter/IO/ThriftIO.fs +++ b/src/FsShelter/IO/ThriftIO.fs @@ -9,8 +9,8 @@ open Prolucid.ThriftShell open TupleSchema open System.IO -type V = Messages.Variant -let vNone = V(None = Messages.NoneStruct()) +type internal V = Messages.Variant +let internal vNone = V(None = Messages.NoneStruct()) let private typeMap = [