Skip to content

Commit acf8c1c

Browse files
committed
Copilot Workspace experiment: ask it to translate temporalio/samples-python#123
1 parent cf47d7c commit acf8c1c

File tree

5 files changed

+200
-0
lines changed

5 files changed

+200
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { proxyActivities } from '@temporalio/workflow';
2+
import { ActivityInput as AllocateNodesToJobInput } from './interfaces';
3+
4+
// Activities with TypeScript syntax and Temporal TypeScript SDK specifics
5+
const { allocateNodesToJob, deallocateNodesForJob, findBadNodes } = proxyActivities<{
6+
allocateNodesToJob(input: AllocateNodesToJobInput): Promise<void>;
7+
deallocateNodesForJob(input: DeallocateNodesForJobInput): Promise<void>;
8+
findBadNodes(input: FindBadNodesInput): Promise<string[]>;
9+
}>({
10+
startToCloseTimeout: '1 minute',
11+
});
12+
13+
export async function allocateNodesToJob(input: AllocateNodesToJobInput): Promise<void> {
14+
console.log(`Assigning nodes ${input.nodes} to job ${input.jobName}`);
15+
await new Promise(resolve => setTimeout(resolve, 100)); // Simulate async operation
16+
}
17+
18+
export async function deallocateNodesForJob(input: DeallocateNodesForJobInput): Promise<void> {
19+
console.log(`Deallocating nodes ${input.nodes} from job ${input.jobName}`);
20+
await new Promise(resolve => setTimeout(resolve, 100)); // Simulate async operation
21+
}
22+
23+
export async function findBadNodes(input: FindBadNodesInput): Promise<string[]> {
24+
await new Promise(resolve => setTimeout(resolve, 100)); // Simulate async operation
25+
const badNodes = input.nodesToCheck.filter(n => parseInt(n) % 5 === 0);
26+
if (badNodes.length) {
27+
console.log(`Found bad nodes: ${badNodes}`);
28+
} else {
29+
console.log("No new bad nodes found.");
30+
}
31+
return badNodes;
32+
}
33+
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { WorkflowClient } from '@temporalio/client';
2+
import { ClusterManagerWorkflow } from './workflow';
3+
import { doClusterLifecycle } from './utils';
4+
5+
async function main() {
6+
const client = new WorkflowClient();
7+
8+
// Define the workflow handle
9+
const workflow = client.createWorkflowHandle(ClusterManagerWorkflow, {
10+
workflowId: 'cluster-management-workflow',
11+
});
12+
13+
// Start the cluster lifecycle
14+
await doClusterLifecycle(workflow);
15+
}
16+
17+
main().catch((err) => {
18+
console.error(err);
19+
process.exit(1);
20+
});
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Worker } from '@temporalio/worker';
2+
import path from 'path';
3+
4+
async function run() {
5+
const worker = await Worker.create({
6+
workflowsPath: path.join(__dirname, './workflows'),
7+
activitiesPath: path.join(__dirname, './activities'),
8+
taskQueue: 'safe-message-handlers-task-queue',
9+
});
10+
11+
await worker.run();
12+
}
13+
14+
run().catch((err) => {
15+
console.error(err);
16+
process.exit(1);
17+
});
+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// This file contains the TypeScript port of the Python workflow for managing cluster updates and signals
2+
3+
import { proxyActivities, defineSignal, defineQuery, setHandler, condition, sleep, defineWorkflow } from '@temporalio/workflow';
4+
import type { AllocateNodesToJobInput, DeallocateNodesForJobInput, FindBadNodesInput } from './interfaces';
5+
6+
// Define signals
7+
const startClusterSignal = defineSignal('startCluster');
8+
const shutdownClusterSignal = defineSignal('shutdownCluster');
9+
const allocateNodesToJobSignal = defineSignal<[AllocateNodesToJobInput]>('allocateNodesToJob');
10+
const deallocateNodesForJobSignal = defineSignal<[DeallocateNodesForJobInput]>('deallocateNodesForJob');
11+
12+
// Define queries
13+
const getClusterStatusQuery = defineQuery<{}>('getClusterStatus');
14+
15+
// Define activities
16+
const { allocateNodesToJob, deallocateNodesForJob, findBadNodes } = proxyActivities<{
17+
allocateNodesToJob(input: AllocateNodesToJobInput): Promise<void>;
18+
deallocateNodesForJob(input: DeallocateNodesForJobInput): Promise<void>;
19+
findBadNodes(input: FindBadNodesInput): Promise<string[]>;
20+
}>({
21+
startToCloseTimeout: '1 minute',
22+
});
23+
24+
// Define workflow interface
25+
export interface ClusterManagerWorkflow {
26+
run(input: ClusterManagerWorkflowInput): Promise<ClusterManagerWorkflowResult>;
27+
}
28+
29+
// Define workflow input and result types
30+
export interface ClusterManagerWorkflowInput {
31+
testContinueAsNew: boolean;
32+
}
33+
34+
export interface ClusterManagerWorkflowResult {
35+
maxAssignedNodes: number;
36+
numCurrentlyAssignedNodes: number;
37+
numBadNodes: number;
38+
}
39+
40+
// Workflow implementation
41+
export const clusterManagerWorkflow: ClusterManagerWorkflow = defineWorkflow({
42+
async run(input: ClusterManagerWorkflowInput) {
43+
let state = {
44+
clusterStarted: false,
45+
clusterShutdown: false,
46+
nodes: {} as Record<string, string | null>,
47+
jobsAdded: new Set<string>(),
48+
maxAssignedNodes: 0,
49+
};
50+
51+
// Signal handlers
52+
setHandler(startClusterSignal, () => {
53+
state.clusterStarted = true;
54+
for (let i = 0; i < 25; i++) {
55+
state.nodes[i.toString()] = null;
56+
}
57+
});
58+
59+
setHandler(shutdownClusterSignal, () => {
60+
state.clusterShutdown = true;
61+
});
62+
63+
setHandler(allocateNodesToJobSignal, async (input: AllocateNodesToJobInput) => {
64+
if (!state.clusterStarted || state.clusterShutdown) {
65+
throw new Error('Cluster is not in a valid state for node allocation');
66+
}
67+
// Allocate nodes to job logic
68+
});
69+
70+
setHandler(deallocateNodesForJobSignal, async (input: DeallocateNodesForJobInput) => {
71+
if (!state.clusterStarted || state.clusterShutdown) {
72+
throw new Error('Cluster is not in a valid state for node deallocation');
73+
}
74+
// Deallocate nodes from job logic
75+
});
76+
77+
// Query handler
78+
setHandler(getClusterStatusQuery, () => {
79+
return {
80+
clusterStarted: state.clusterStarted,
81+
clusterShutdown: state.clusterShutdown,
82+
numNodes: Object.keys(state.nodes).length,
83+
numAssignedNodes: Object.values(state.nodes).filter(n => n !== null).length,
84+
};
85+
});
86+
87+
// Main workflow logic
88+
await condition(() => state.clusterStarted, 'Waiting for cluster to start');
89+
// Perform operations while cluster is active
90+
while (!state.clusterShutdown) {
91+
// Example: perform periodic health checks
92+
await sleep(60000); // Sleep for 60 seconds
93+
}
94+
95+
// Return workflow result
96+
return {
97+
maxAssignedNodes: state.maxAssignedNodes,
98+
numCurrentlyAssignedNodes: Object.values(state.nodes).filter(n => n !== null).length,
99+
numBadNodes: Object.values(state.nodes).filter(n => n === 'BAD').length,
100+
};
101+
},
102+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { WorkflowClient } from '@temporalio/client';
2+
import { ClusterManagerWorkflow } from './workflow';
3+
import { v4 as uuidv4 } from 'uuid';
4+
5+
async function run() {
6+
const client = new WorkflowClient();
7+
8+
// Define the workflow handle
9+
const workflow = client.createWorkflowHandle(ClusterManagerWorkflow, {
10+
workflowId: `cluster-management-workflow-${uuidv4()}`,
11+
});
12+
13+
// Test workflow functionality
14+
await workflow.start();
15+
await workflow.signal.startCluster();
16+
await workflow.executeUpdate('allocateNodesToJob', {
17+
numNodes: 5,
18+
jobName: 'job1',
19+
});
20+
await workflow.signal.shutdownCluster();
21+
const result = await workflow.result();
22+
console.log('Workflow result:', result);
23+
}
24+
25+
run().catch((err) => {
26+
console.error(err);
27+
process.exit(1);
28+
});

0 commit comments

Comments
 (0)