forked from Texera/texera
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathQ2_Final.json
1 lines (1 loc) · 9.23 KB
/
Q2_Final.json
1
{"operators":[{"operatorID":"ScalaUDF-operator-62b8ecc5-9699-4c0f-860d-32bc9586c908","operatorType":"ScalaUDF","operatorVersion":"bfcdd448e9d4ff69a973c29ccf008ec9b5baed71","operatorProperties":{"code":"import edu.uci.ics.amber.engine.common.{CheckpointState, CheckpointSupport}\nimport edu.uci.ics.amber.engine.common.tuple.amber.TupleLike\nimport edu.uci.ics.texera.workflow.common.tuple.Tuple\nimport edu.uci.ics.texera.workflow.common.operators.OperatorExecutor\nimport scala.collection.mutable\nimport edu.uci.ics.amber.engine.common.workflow.PortIdentity\n\nclass ScalaUDFOpExec extends OperatorExecutor with CheckpointSupport {\n\n // Initial user account dictionary with balances\n var userBalances: mutable.Map[String, Int] = mutable.Map(\n \"Group1-Alice\" -> 293,\n \"Group1-Bob\" -> 123,\n \"Group2-Charlie\" -> 492,\n \"Group2-Dave\" -> 284\n )\n\n // Process tuples with \"from\" and \"to\" fields, updating balances\n override def processTuple(tuple: Tuple, port: Int): Iterator[TupleLike] = {\n // Extract fields from the input tuple (assuming the structure: transactionID, amount, from, to, timestamp)\n val transactionID = tuple.getFields(0).asInstanceOf[String]\n val amount = tuple.getFields(1).asInstanceOf[Int]\n val from = tuple.getFields(2).asInstanceOf[String]\n val to = tuple.getFields(3).asInstanceOf[String]\n\n val matchingFromUsers = userBalances.keys.filter(_.contains(from)).toSeq\n\n // If no users are found for the 'from' string, return an empty iterator\n if (matchingFromUsers.isEmpty) return Iterator.empty\n\n // Calculate the split amount for each 'from' user\n val fromUserCount = matchingFromUsers.size\n val splitAmount = amount / fromUserCount\n\n // Deduct split amount from each matching 'from' user\n val outputTuples = matchingFromUsers.flatMap { fromUser =>\n \n userBalances(fromUser) = userBalances.getOrElse(fromUser, 0) - splitAmount\n if (userBalances(fromUser) < 0) {\n // Output a tuple if any user has insufficient balance\n Some(TupleLike(fromUser, userBalances.getOrElse(fromUser, 0)))\n }else{\n None\n }\n }\n\n // Find all users whose name contains the 'to' string\n val matchingToUsers = userBalances.keys.filter(_.contains(to)).toSeq\n\n // Handle empty string case for 'to' user\n if (to.isEmpty) return Iterator.empty\n\n // Split the amount to credit to each 'to' user\n val toUserCount = matchingToUsers.size\n val creditAmount = amount / toUserCount\n\n // Credit split amount to each matching 'to' user\n matchingToUsers.foreach { toUser =>\n userBalances(toUser) = userBalances.getOrElse(toUser, 0) + creditAmount\n }\n\n // Return an iterator over all output tuples for users with insufficient balance\n outputTuples.iterator\n }\n\n // Serialize the state to checkpoint\n override def serializeState(\n currentIteratorState: Iterator[(TupleLike, Option[PortIdentity])],\n checkpoint: CheckpointState\n ): Iterator[(TupleLike, Option[PortIdentity])] = {\n checkpoint.save(\"userBalances\", userBalances)\n currentIteratorState\n }\n\n // Deserialize the state from checkpoint\n override def deserializeState(\n checkpoint: CheckpointState\n ): Iterator[(TupleLike, Option[PortIdentity])] = {\n userBalances = checkpoint.load(\"userBalances\").asInstanceOf[mutable.Map[String, Int]]\n Iterator.empty\n }\n\n override def getEstimatedCheckpointCost: Long = 0L\n\n // Get a string representation of the current state\n override def getState: String = {\n // Convert the current state (Map) to a readable string format\n userBalances.map { case (user, balance) => s\"User: $user, Balance: $balance\" }.mkString(\"\\n\") + s\"\\nsum of balance: ${userBalances.map(x => x._2).sum}\"\n }\n}\n","workers":1,"retainInputColumns":false,"outputColumns":[{"attributeName":"user","attributeType":"string"},{"attributeName":"balance","attributeType":"integer"}]},"inputPorts":[{"portID":"input-0","displayName":"","allowMultiInputs":true,"isDynamicPort":false,"dependencies":[]}],"outputPorts":[{"portID":"output-0","displayName":"","allowMultiInputs":false,"isDynamicPort":false}],"showAdvanced":false,"isDisabled":false,"customDisplayName":"Scala UDF","dynamicInputPorts":true,"dynamicOutputPorts":true},{"operatorID":"ScalaUDFSource-operator-2e26f979-cbf8-40ce-8452-fc46b26cea3d","operatorType":"ScalaUDFSource","operatorVersion":"bfcdd448e9d4ff69a973c29ccf008ec9b5baed71","operatorProperties":{"code":"import edu.uci.ics.amber.engine.common.{CheckpointState, CheckpointSupport}\nimport edu.uci.ics.amber.engine.common.SourceOperatorExecutor\nimport edu.uci.ics.amber.engine.common.tuple.amber.TupleLike\nimport edu.uci.ics.amber.engine.common.workflow.PortIdentity\nimport scala.concurrent.duration._\nimport java.time.Instant\nimport scala.util.Random\n\nclass ScalaUDFOpExec extends SourceOperatorExecutor with CheckpointSupport {\n\n // Variable to maintain the current index of tuple production\n var currentIndex: Int = 0\n val totalTuplesToProduce: Int = 1000 // Total number of tuples to produce\n val errorFrequency: Int = 379 // Introduce an error tuple every 50 tuples\n val groups: Seq[String] = Seq(\"Group1-Alice\", \"Group1-Bob\", \"Group2-Charlie\", \"Group2-Dave\", \"Group1\", \"Group2\")\n\n // Set a specific seed for random number generation to make it deterministic\n var random = new Random(12345L) // 12345L is the seed\n\n // Produce tuples lazily using an iterator, wrapping all computation within the iterator\n override def produceTuple(): Iterator[TupleLike] = new Iterator[TupleLike] {\n \n // Return true if more tuples need to be produced\n override def hasNext: Boolean = currentIndex < totalTuplesToProduce\n\n // Generate the next tuple\n override def next(): TupleLike = {\n val tupleLike = if (currentIndex % errorFrequency == 0 && currentIndex != 0) {\n // Create an error tuple every `errorFrequency` tuples\n createErrorTransaction()\n } else {\n // Create a valid transaction tuple\n val validTuple = createValidTransaction()\n validTuple\n }\n\n // Update the current index\n currentIndex += 1\n\n // Introduce a 0.1-second delay between each tuple\n Thread.sleep(500)\n\n // Return the generated tuple\n tupleLike\n }\n }\n\n // Method to create a valid transaction and return it as a TupleLike\n private def createValidTransaction(): TupleLike = {\n val transactionID = s\"txn-${currentIndex}\"\n val amount = 100 // Random amount between 0 and 1000\n // Returning the data directly as TupleLike\n // Select random 'from' and 'to' group names\n val fromGroup = groups(random.nextInt(groups.length))\n var toGroup = groups(random.nextInt(groups.length))\n TupleLike(transactionID, amount, fromGroup, toGroup)\n }\n\n\n // Method to create an error transaction and return it as a TupleLike\n private def createErrorTransaction(): TupleLike = {\n TupleLike(\"txn\", 9999999, \"\", \"\")\n }\n\n // Serialize the state (saving the current index, accumulated sum, total time gap, and transaction count to the checkpoint)\n override def serializeState(\n currentIteratorState: Iterator[(TupleLike, Option[PortIdentity])],\n checkpoint: CheckpointState\n ): Iterator[(TupleLike, Option[PortIdentity])] = {\n // Save important states to the checkpoint\n checkpoint.save(\"currentIndex\", currentIndex)\n checkpoint.save(\"random\", random)\n\n currentIteratorState\n }\n\n // Deserialize the state (restoring the current index, accumulated sum, total time gap, and transaction count from the checkpoint)\n override def deserializeState(\n checkpoint: CheckpointState\n ): Iterator[(TupleLike, Option[PortIdentity])] = {\n // Restore the saved states from the checkpoint\n currentIndex = checkpoint.load(\"currentIndex\").asInstanceOf[Int]\n random = checkpoint.load(\"random\").asInstanceOf[Random]\n \n // Continue producing tuples starting from the restored index\n produceTuple().map(tuple => (tuple, Option.empty))\n }\n\n // Estimate the cost of checkpointing (you can customize this based on the use case)\n override def getEstimatedCheckpointCost: Long = 0L\n\n // Override getState to show accumulated sum and average gap between transactions\n override def getState: String = \"\"\n}\n","workers":1,"columns":[{"attributeName":"transactionID","attributeType":"string"},{"attributeName":"amount","attributeType":"integer"},{"attributeName":"from","attributeType":"string"},{"attributeName":"to","attributeType":"string"}]},"inputPorts":[],"outputPorts":[{"portID":"output-0","displayName":"","allowMultiInputs":false,"isDynamicPort":false}],"showAdvanced":false,"isDisabled":false,"customDisplayName":"1-out Scala UDF","dynamicInputPorts":false,"dynamicOutputPorts":false}],"operatorPositions":{"ScalaUDF-operator-62b8ecc5-9699-4c0f-860d-32bc9586c908":{"x":-32,"y":-89},"ScalaUDFSource-operator-2e26f979-cbf8-40ce-8452-fc46b26cea3d":{"x":-259,"y":-89}},"links":[{"linkID":"link-10c70aeb-13cf-4c46-80d9-482f95b84b30","source":{"operatorID":"ScalaUDFSource-operator-2e26f979-cbf8-40ce-8452-fc46b26cea3d","portID":"output-0"},"target":{"operatorID":"ScalaUDF-operator-62b8ecc5-9699-4c0f-860d-32bc9586c908","portID":"input-0"}}],"groups":[],"commentBoxes":[]}