Skip to content

Commit 9decb2d

Browse files
authored
Use existing staging client for dataproc staging and improve staging client for s3 (#1063)
Signed-off-by: Khor Shu Heng <khor.heng@gojek.com> Co-authored-by: Khor Shu Heng <khor.heng@gojek.com>
1 parent 7831768 commit 9decb2d

2 files changed

Lines changed: 42 additions & 9 deletions

File tree

sdk/python/feast/pyspark/launchers/gcloud/dataproc.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from urllib.parse import urlparse
55

66
from google.api_core.operation import Operation
7-
from google.cloud import dataproc_v1, storage
7+
from google.cloud import dataproc_v1
88
from google.cloud.dataproc_v1 import Job as DataprocJob
99
from google.cloud.dataproc_v1 import JobStatus
1010

@@ -18,6 +18,7 @@
1818
SparkJobParameters,
1919
SparkJobStatus,
2020
)
21+
from feast.staging.storage_client import get_staging_client
2122

2223

2324
class DataprocJobMixin:
@@ -113,13 +114,11 @@ def __init__(
113114
)
114115

115116
def _stage_files(self, pyspark_script: str, job_id: str) -> str:
116-
client = storage.Client()
117-
bucket = client.get_bucket(self.staging_bucket)
117+
staging_client = get_staging_client("gs")
118118
blob_path = os.path.join(
119119
self.remote_path, job_id, os.path.basename(pyspark_script),
120120
)
121-
blob = bucket.blob(blob_path)
122-
blob.upload_from_filename(pyspark_script)
121+
staging_client.upload_file(blob_path, self.staging_bucket, pyspark_script)
123122

124123
return f"gs://{self.staging_bucket}/{blob_path}"
125124

sdk/python/feast/staging/storage_client.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
15-
16-
15+
import hashlib
1716
import os
1817
import re
1918
import shutil
@@ -192,6 +191,13 @@ def list_files(self, bucket: str, path: str) -> List[str]:
192191
else:
193192
return [f"{S3}://{bucket}/{path.lstrip('/')}"]
194193

194+
def _hash_file(self, local_path: str):
195+
h = hashlib.sha256()
196+
with open(local_path, "rb") as f:
197+
for block in iter(lambda: f.read(2 ** 20), b""):
198+
h.update(block)
199+
return h.hexdigest()
200+
195201
def upload_file(self, local_path: str, bucket: str, remote_path: str):
196202
"""
197203
Uploads file to s3.
@@ -201,8 +207,36 @@ def upload_file(self, local_path: str, bucket: str, remote_path: str):
201207
bucket (str): s3 Bucket name
202208
remote_path (str): relative path to the folder to which the files need to be uploaded
203209
"""
204-
with open(local_path, "rb") as file:
205-
self.s3_client.upload_fileobj(file, bucket, remote_path)
210+
211+
sha256sum = self._hash_file(local_path)
212+
213+
import botocore
214+
215+
try:
216+
head_response = self.s3_client.head_object(Bucket=bucket, Key=remote_path)
217+
if head_response["Metadata"]["sha256sum"] == sha256sum:
218+
# File already exists
219+
return remote_path
220+
else:
221+
print(f"Uploading {local_path} to {remote_path}")
222+
self.s3_client.upload_file(
223+
local_path,
224+
bucket,
225+
remote_path,
226+
ExtraArgs={"Metadata": {"sha256sum": sha256sum}},
227+
)
228+
return remote_path
229+
except botocore.exceptions.ClientError as e:
230+
if e.response["Error"]["Code"] != "404":
231+
raise
232+
233+
self.s3_client.upload_file(
234+
local_path,
235+
bucket,
236+
remote_path,
237+
ExtraArgs={"Metadata": {"sha256sum": sha256sum}},
238+
)
239+
return remote_path
206240

207241

208242
class LocalFSClient(AbstractStagingClient):

0 commit comments

Comments
 (0)