2525"""
2626
2727import shutil
28+ import threading
2829from typing import Iterator
2930from typing import Optional
3031
@@ -39,13 +40,16 @@ class UserPipelineTracker:
3940 derived pipelines.
4041 """
4142 def __init__ (self ):
43+ self ._lock = threading .RLock ()
4244 self ._user_pipelines : dict [beam .Pipeline , list [beam .Pipeline ]] = {}
43- self ._derived_pipelines : dict [beam .Pipeline ] = {}
44- self ._pid_to_pipelines : dict [beam .Pipeline ] = {}
45+ self ._derived_pipelines : dict [beam .Pipeline , beam . Pipeline ] = {}
46+ self ._pid_to_pipelines : dict [str , beam .Pipeline ] = {}
4547
4648 def __iter__ (self ) -> Iterator [beam .Pipeline ]:
4749 """Iterates through all the user pipelines."""
48- for p in self ._user_pipelines :
50+ with self ._lock :
51+ pipelines = list (self ._user_pipelines .keys ())
52+ for p in pipelines :
4953 yield p
5054
5155 def _key (self , pipeline : beam .Pipeline ) -> str :
@@ -57,45 +61,57 @@ def evict(self, pipeline: beam.Pipeline) -> None:
5761 Removes the given pipeline and derived pipelines if a user pipeline.
5862 Otherwise, removes the given derived pipeline.
5963 """
60- user_pipeline = self .get_user_pipeline (pipeline )
61- if user_pipeline :
62- for d in self ._user_pipelines [user_pipeline ]:
63- del self ._derived_pipelines [d ]
64- del self ._user_pipelines [user_pipeline ]
65- elif pipeline in self ._derived_pipelines :
66- del self ._derived_pipelines [pipeline ]
64+ with self ._lock :
65+ if pipeline in self ._user_pipelines :
66+ for d in self ._user_pipelines [pipeline ]:
67+ self ._derived_pipelines .pop (d , None )
68+ self ._pid_to_pipelines .pop (self ._key (d ), None )
69+ self ._user_pipelines .pop (pipeline , None )
70+ elif pipeline in self ._derived_pipelines :
71+ user_pipeline = self ._derived_pipelines .pop (pipeline , None )
72+ if user_pipeline in self ._user_pipelines :
73+ try :
74+ self ._user_pipelines [user_pipeline ].remove (pipeline )
75+ except ValueError :
76+ pass
77+ self ._pid_to_pipelines .pop (self ._key (pipeline ), None )
6778
6879 def clear (self ) -> None :
6980 """Clears the tracker of all user and derived pipelines."""
7081 # Remove all local_tempdir of created pipelines.
71- for p in self ._pid_to_pipelines .values ():
72- shutil .rmtree (p .local_tempdir , ignore_errors = True )
82+ with self ._lock :
83+ pipelines = list (self ._pid_to_pipelines .values ())
84+ self ._user_pipelines .clear ()
85+ self ._derived_pipelines .clear ()
86+ self ._pid_to_pipelines .clear ()
7387
74- self ._user_pipelines .clear ()
75- self ._derived_pipelines .clear ()
76- self ._pid_to_pipelines .clear ()
88+ for p in pipelines :
89+ shutil .rmtree (p .local_tempdir , ignore_errors = True )
7790
7891 def get_pipeline (self , pid : str ) -> Optional [beam .Pipeline ]:
7992 """Returns the pipeline corresponding to the given pipeline id."""
80- return self ._pid_to_pipelines .get (pid , None )
93+ with self ._lock :
94+ return self ._pid_to_pipelines .get (pid , None )
8195
8296 def add_user_pipeline (self , p : beam .Pipeline ) -> beam .Pipeline :
8397 """Adds a user pipeline with an empty set of derived pipelines."""
84- self ._memoize_pipieline (p )
98+ with self ._lock :
99+ self ._memoize_pipeline (p )
85100
86- # Create a new node for the user pipeline if it doesn't exist already.
87- user_pipeline = self .get_user_pipeline (p )
88- if not user_pipeline :
89- user_pipeline = p
90- self ._user_pipelines [p ] = []
101+ # Create a new node for the user pipeline if it doesn't exist already.
102+ user_pipeline = self .get_user_pipeline (p )
103+ if not user_pipeline :
104+ user_pipeline = p
105+ self ._user_pipelines [p ] = []
91106
92- return user_pipeline
107+ return user_pipeline
93108
94- def _memoize_pipieline (self , p : beam .Pipeline ) -> None :
109+ def _memoize_pipeline (self , p : beam .Pipeline ) -> None :
95110 """Memoizes the pid of the pipeline to the pipeline object."""
96111 pid = self ._key (p )
97- if pid not in self ._pid_to_pipelines :
98- self ._pid_to_pipelines [pid ] = p
112+ with self ._lock :
113+ if pid not in self ._pid_to_pipelines :
114+ self ._pid_to_pipelines [pid ] = p
99115
100116 def add_derived_pipeline (
101117 self , maybe_user_pipeline : beam .Pipeline ,
@@ -119,20 +135,21 @@ def add_derived_pipeline(
119135 # Returns p.
120136 ut.get_user_pipeline(derived2)
121137 """
122- self ._memoize_pipieline (maybe_user_pipeline )
123- self ._memoize_pipieline (derived_pipeline )
138+ with self ._lock :
139+ self ._memoize_pipeline (maybe_user_pipeline )
140+ self ._memoize_pipeline (derived_pipeline )
124141
125- # Cannot add a derived pipeline twice.
126- assert derived_pipeline not in self ._derived_pipelines
142+ # Cannot add a derived pipeline twice.
143+ assert derived_pipeline not in self ._derived_pipelines
127144
128- # Get the "true" user pipeline. This allows for the user to derive a
129- # pipeline from another derived pipeline, use both as arguments, and this
130- # method will still get the correct user pipeline.
131- user = self .add_user_pipeline (maybe_user_pipeline )
145+ # Get the "true" user pipeline. This allows for the user to derive a
146+ # pipeline from another derived pipeline, use both as arguments, and this
147+ # method will still get the correct user pipeline.
148+ user = self .add_user_pipeline (maybe_user_pipeline )
132149
133- # Map the derived pipeline to the user pipeline.
134- self ._derived_pipelines [derived_pipeline ] = user
135- self ._user_pipelines [user ].append (derived_pipeline )
150+ # Map the derived pipeline to the user pipeline.
151+ self ._derived_pipelines [derived_pipeline ] = user
152+ self ._user_pipelines [user ].append (derived_pipeline )
136153
137154 def get_user_pipeline (self , p : beam .Pipeline ) -> Optional [beam .Pipeline ]:
138155 """Returns the user pipeline of the given pipeline.
@@ -142,14 +159,14 @@ def get_user_pipeline(self, p: beam.Pipeline) -> Optional[beam.Pipeline]:
142159 returns the same pipeline. If the given pipeline is a derived pipeline then
143160 this returns the user pipeline.
144161 """
162+ with self ._lock :
163+ # If `p` is a user pipeline then return it.
164+ if p in self ._user_pipelines :
165+ return p
145166
146- # If `p` is a user pipeline then return it.
147- if p in self ._user_pipelines :
148- return p
149-
150- # If `p` exists then return its user pipeline.
151- if p in self ._derived_pipelines :
152- return self ._derived_pipelines [p ]
167+ # If `p` exists then return its user pipeline.
168+ if p in self ._derived_pipelines :
169+ return self ._derived_pipelines [p ]
153170
154- # Otherwise, `p` is not in this tracker.
155- return None
171+ # Otherwise, `p` is not in this tracker.
172+ return None
0 commit comments