This is an automated email from the ASF dual-hosted git repository.
yhu pushed a commit to branch release-2.63
in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/release-2.63 by this push:
new e285617d304 Revert emitting lineage as boundedtrie from python (#33935)
e285617d304 is described below
commit e285617d30432ab3e13c29a152c6ffbe62672bee
Author: Rohit <[email protected]>
AuthorDate: Tue Feb 11 07:30:30 2025 -0800
Revert emitting lineage as boundedtrie from python (#33935)
* Revert "Place bounded trie lineage metrics under a new name."
This reverts commit 3210b47d94ea9cd032eb6b75904fb25b3da47dd4.
* Revert "Merge pull request #33381 Migrate lineage counters to bounded
tries."
This reverts commit e8df26fcea46e3ca38d02ac1263389dbf859472c, reversing
changes made to 8fee3ca5753ed2ca7563e46ce17cef4d9d95e3cc.
---
sdks/python/apache_beam/io/aws/s3filesystem.py | 10 ++--
.../python/apache_beam/io/aws/s3filesystem_test.py | 3 +-
.../apache_beam/io/azure/blobstoragefilesystem.py | 10 ++--
.../io/azure/blobstoragefilesystem_test.py | 3 +-
sdks/python/apache_beam/io/filebasedsink.py | 20 +++++--
sdks/python/apache_beam/io/filebasedsource.py | 53 ++++++++++++++++-
sdks/python/apache_beam/io/filesystem.py | 6 +-
sdks/python/apache_beam/io/filesystems.py | 16 ++++--
sdks/python/apache_beam/io/gcp/bigquery.py | 2 +-
sdks/python/apache_beam/io/gcp/gcsfilesystem.py | 10 ++--
.../apache_beam/io/gcp/gcsfilesystem_test.py | 3 +-
sdks/python/apache_beam/io/localfilesystem.py | 3 -
sdks/python/apache_beam/metrics/cells.py | 7 +--
sdks/python/apache_beam/metrics/metric.py | 67 +++++-----------------
sdks/python/apache_beam/metrics/metric_test.py | 11 +---
15 files changed, 118 insertions(+), 106 deletions(-)
diff --git a/sdks/python/apache_beam/io/aws/s3filesystem.py
b/sdks/python/apache_beam/io/aws/s3filesystem.py
index 584263ec241..ffbce5893a9 100644
--- a/sdks/python/apache_beam/io/aws/s3filesystem.py
+++ b/sdks/python/apache_beam/io/aws/s3filesystem.py
@@ -18,7 +18,6 @@
"""S3 file system implementation for accessing files on AWS S3."""
# pytype: skip-file
-import traceback
from apache_beam.io.aws import s3io
from apache_beam.io.filesystem import BeamIOError
@@ -316,13 +315,14 @@ class S3FileSystem(FileSystem):
if exceptions:
raise BeamIOError("Delete operation failed", exceptions)
- def report_lineage(self, path, lineage):
+ def report_lineage(self, path, lineage, level=None):
try:
components = s3io.parse_s3_path(path, object_optional=True)
except ValueError:
# report lineage is fail-safe
- traceback.print_exc()
return
- if components and not components[-1]:
+ if level == FileSystem.LineageLevel.TOP_LEVEL or \
+ (len(components) > 1 and components[-1] == ''):
+ # bucket only
components = components[:-1]
- lineage.add('s3', *components, last_segment_sep='/')
+ lineage.add('s3', *components)
diff --git a/sdks/python/apache_beam/io/aws/s3filesystem_test.py
b/sdks/python/apache_beam/io/aws/s3filesystem_test.py
index 036727cd7a7..87403f482bd 100644
--- a/sdks/python/apache_beam/io/aws/s3filesystem_test.py
+++ b/sdks/python/apache_beam/io/aws/s3filesystem_test.py
@@ -272,8 +272,7 @@ class S3FileSystemTest(unittest.TestCase):
def _verify_lineage(self, uri, expected_segments):
lineage_mock = mock.MagicMock()
self.fs.report_lineage(uri, lineage_mock)
- lineage_mock.add.assert_called_once_with(
- "s3", *expected_segments, last_segment_sep='/')
+ lineage_mock.add.assert_called_once_with("s3", *expected_segments)
if __name__ == '__main__':
diff --git a/sdks/python/apache_beam/io/azure/blobstoragefilesystem.py
b/sdks/python/apache_beam/io/azure/blobstoragefilesystem.py
index 4b7462cae03..4495245dc54 100644
--- a/sdks/python/apache_beam/io/azure/blobstoragefilesystem.py
+++ b/sdks/python/apache_beam/io/azure/blobstoragefilesystem.py
@@ -18,7 +18,6 @@
"""Azure Blob Storage Implementation for accesing files on
Azure Blob Storage.
"""
-import traceback
from apache_beam.io.azure import blobstorageio
from apache_beam.io.filesystem import BeamIOError
@@ -318,14 +317,15 @@ class BlobStorageFileSystem(FileSystem):
if exceptions:
raise BeamIOError("Delete operation failed", exceptions)
- def report_lineage(self, path, lineage):
+ def report_lineage(self, path, lineage, level=None):
try:
components = blobstorageio.parse_azfs_path(
path, blob_optional=True, get_account=True)
except ValueError:
# report lineage is fail-safe
- traceback.print_exc()
return
- if components and not components[-1]:
+ if level == FileSystem.LineageLevel.TOP_LEVEL \
+ or(len(components) > 1 and components[-1] == ''):
+ # bucket only
components = components[:-1]
- lineage.add('abs', *components, last_segment_sep='/')
+ lineage.add('abs', *components)
diff --git a/sdks/python/apache_beam/io/azure/blobstoragefilesystem_test.py
b/sdks/python/apache_beam/io/azure/blobstoragefilesystem_test.py
index c3418e137e8..138fe5f78b2 100644
--- a/sdks/python/apache_beam/io/azure/blobstoragefilesystem_test.py
+++ b/sdks/python/apache_beam/io/azure/blobstoragefilesystem_test.py
@@ -330,8 +330,7 @@ class BlobStorageFileSystemTest(unittest.TestCase):
def _verify_lineage(self, uri, expected_segments):
lineage_mock = mock.MagicMock()
self.fs.report_lineage(uri, lineage_mock)
- lineage_mock.add.assert_called_once_with(
- "abs", *expected_segments, last_segment_sep='/')
+ lineage_mock.add.assert_called_once_with("abs", *expected_segments)
if __name__ == '__main__':
diff --git a/sdks/python/apache_beam/io/filebasedsink.py
b/sdks/python/apache_beam/io/filebasedsink.py
index eb433bd6058..f9d4303c8c7 100644
--- a/sdks/python/apache_beam/io/filebasedsink.py
+++ b/sdks/python/apache_beam/io/filebasedsink.py
@@ -286,16 +286,24 @@ class FileBasedSink(iobase.Sink):
def _report_sink_lineage(self, dst_glob, dst_files):
"""
- Report sink Lineage. Report every file if number of files no more than 10,
- otherwise only report glob.
+ Report sink Lineage. Report every file if number of files no more than 100,
+ otherwise only report at directory level.
"""
- # There is rollup at the higher level, but this loses glob information.
- # Better to report multiple globs than just the parent directory.
- if len(dst_files) <= 10:
+ if len(dst_files) <= 100:
for dst in dst_files:
FileSystems.report_sink_lineage(dst)
else:
- FileSystems.report_sink_lineage(dst_glob)
+ dst = dst_glob
+ # dst_glob has a wildcard for shard number (see _shard_name_template)
+ sep = dst_glob.find('*')
+ if sep > 0:
+ dst = dst[:sep]
+ try:
+ dst, _ = FileSystems.split(dst)
+ except ValueError:
+ return # lineage report is fail-safe
+
+ FileSystems.report_sink_lineage(dst)
@check_accessible(['file_path_prefix'])
def finalize_write(
diff --git a/sdks/python/apache_beam/io/filebasedsource.py
b/sdks/python/apache_beam/io/filebasedsource.py
index 49b1b1d125f..a02bc6de32c 100644
--- a/sdks/python/apache_beam/io/filebasedsource.py
+++ b/sdks/python/apache_beam/io/filebasedsource.py
@@ -39,6 +39,7 @@ from apache_beam.io import iobase
from apache_beam.io import range_trackers
from apache_beam.io.filesystem import CompressionTypes
from apache_beam.io.filesystem import FileMetadata
+from apache_beam.io.filesystem import FileSystem
from apache_beam.io.filesystems import FileSystems
from apache_beam.io.restriction_trackers import OffsetRange
from apache_beam.options.value_provider import StaticValueProvider
@@ -169,11 +170,37 @@ class FileBasedSource(iobase.BoundedSource):
splittable=splittable)
single_file_sources.append(single_file_source)
- FileSystems.report_source_lineage(pattern)
+ self._report_source_lineage(files_metadata)
self._concat_source = concat_source.ConcatSource(single_file_sources)
return self._concat_source
+ def _report_source_lineage(self, files_metadata):
+ """
+ Report source Lineage. depend on the number of files, report full file
+ name, only dir, or only top level
+ """
+ if len(files_metadata) <= 100:
+ for file_metadata in files_metadata:
+ FileSystems.report_source_lineage(file_metadata.path)
+ else:
+ size_track = set()
+ for file_metadata in files_metadata:
+ if len(size_track) >= 100:
+ FileSystems.report_source_lineage(
+ file_metadata.path, level=FileSystem.LineageLevel.TOP_LEVEL)
+ return
+
+ try:
+ base, _ = FileSystems.split(file_metadata.path)
+ except ValueError:
+ pass
+ else:
+ size_track.add(base)
+
+ for base in size_track:
+ FileSystems.report_source_lineage(base)
+
def open_file(self, file_name):
return FileSystems.open(
file_name,
@@ -355,7 +382,7 @@ class _ExpandIntoRanges(DoFn):
match_results = FileSystems.match([element])
metadata_list = match_results[0].metadata_list
for metadata in metadata_list:
- FileSystems.report_source_lineage(metadata.path)
+ self._report_source_lineage(metadata.path)
splittable = (
self._splittable and _determine_splittability_from_compression_type(
@@ -370,6 +397,28 @@ class _ExpandIntoRanges(DoFn):
metadata,
OffsetRange(0, range_trackers.OffsetRangeTracker.OFFSET_INFINITY))
+ def _report_source_lineage(self, path):
+ """
+ Report source Lineage. Due to the size limit of Beam metrics, report full
+ file name or only top level depend on the number of files.
+
+ * Number of files<=100, report full file paths;
+
+ * Otherwise, report top level only.
+ """
+ if self._size_track is None:
+ self._size_track = set()
+ elif len(self._size_track) == 0:
+ FileSystems.report_source_lineage(
+ path, level=FileSystem.LineageLevel.TOP_LEVEL)
+ return
+
+ self._size_track.add(path)
+ FileSystems.report_source_lineage(path)
+
+ if len(self._size_track) >= 100:
+ self._size_track.clear()
+
class _ReadRange(DoFn):
def __init__(
diff --git a/sdks/python/apache_beam/io/filesystem.py
b/sdks/python/apache_beam/io/filesystem.py
index bdc25dcf0fe..840fdf3309e 100644
--- a/sdks/python/apache_beam/io/filesystem.py
+++ b/sdks/python/apache_beam/io/filesystem.py
@@ -934,7 +934,11 @@ class FileSystem(BeamPlugin, metaclass=abc.ABCMeta):
"""
raise NotImplementedError
- def report_lineage(self, path, unused_lineage):
+ class LineageLevel:
+ FILE = 'FILE'
+ TOP_LEVEL = 'TOP_LEVEL'
+
+ def report_lineage(self, path, unused_lineage, level=None):
"""
Report Lineage metrics for path.
diff --git a/sdks/python/apache_beam/io/filesystems.py
b/sdks/python/apache_beam/io/filesystems.py
index 1d64f88684b..87f45f3308e 100644
--- a/sdks/python/apache_beam/io/filesystems.py
+++ b/sdks/python/apache_beam/io/filesystems.py
@@ -391,21 +391,27 @@ class FileSystems(object):
return filesystem.CHUNK_SIZE
@staticmethod
- def report_source_lineage(path):
+ def report_source_lineage(path, level=None):
"""
- Report source :class:`~apache_beam.metrics.metric.Lineage`.
+ Report source :class:`~apache_beam.metrics.metric.LineageLevel`.
Args:
path: string path to be reported.
+ level: the level of file path. default to
+ :class:`~apache_beam.io.filesystem.FileSystem.LineageLevel`.FILE.
"""
- FileSystems.get_filesystem(path).report_lineage(path, Lineage.sources())
+ filesystem = FileSystems.get_filesystem(path)
+ filesystem.report_lineage(path, Lineage.sources(), level=level)
@staticmethod
- def report_sink_lineage(path):
+ def report_sink_lineage(path, level=None):
"""
Report sink :class:`~apache_beam.metrics.metric.Lineage`.
Args:
path: string path to be reported.
+ level: the level of file path. default to
+ :class:`~apache_beam.io.filesystem.FileSystem.Lineage`.FILE.
"""
- FileSystems.get_filesystem(path).report_lineage(path, Lineage.sinks())
+ filesystem = FileSystems.get_filesystem(path)
+ filesystem.report_lineage(path, Lineage.sinks(), level=level)
diff --git a/sdks/python/apache_beam/io/gcp/bigquery.py
b/sdks/python/apache_beam/io/gcp/bigquery.py
index 9f60b5af672..11e0d098b2f 100644
--- a/sdks/python/apache_beam/io/gcp/bigquery.py
+++ b/sdks/python/apache_beam/io/gcp/bigquery.py
@@ -1163,7 +1163,7 @@ class _CustomBigQueryStorageSource(BoundedSource):
self.table_reference.datasetId,
self.table_reference.tableId)
Lineage.sources().add(
- 'bigquery',
+ "bigquery",
self.table_reference.projectId,
self.table_reference.datasetId,
self.table_reference.tableId)
diff --git a/sdks/python/apache_beam/io/gcp/gcsfilesystem.py
b/sdks/python/apache_beam/io/gcp/gcsfilesystem.py
index 3763e21abc9..d48a9ab02b0 100644
--- a/sdks/python/apache_beam/io/gcp/gcsfilesystem.py
+++ b/sdks/python/apache_beam/io/gcp/gcsfilesystem.py
@@ -26,7 +26,6 @@ https://github.com/apache/beam/blob/master/sdks/python/OWNERS
# pytype: skip-file
-import traceback
from typing import BinaryIO # pylint: disable=unused-import
from apache_beam.io.filesystem import BeamIOError
@@ -368,13 +367,14 @@ class GCSFileSystem(FileSystem):
if exceptions:
raise BeamIOError("Delete operation failed", exceptions)
- def report_lineage(self, path, lineage):
+ def report_lineage(self, path, lineage, level=None):
try:
components = gcsio.parse_gcs_path(path, object_optional=True)
except ValueError:
# report lineage is fail-safe
- traceback.print_exc()
return
- if components and not components[-1]:
+ if level == FileSystem.LineageLevel.TOP_LEVEL \
+ or(len(components) > 1 and components[-1] == ''):
+ # bucket only
components = components[:-1]
- lineage.add('gcs', *components, last_segment_sep='/')
+ lineage.add('gcs', *components)
diff --git a/sdks/python/apache_beam/io/gcp/gcsfilesystem_test.py
b/sdks/python/apache_beam/io/gcp/gcsfilesystem_test.py
index ade8529dcac..ec7fa94b05f 100644
--- a/sdks/python/apache_beam/io/gcp/gcsfilesystem_test.py
+++ b/sdks/python/apache_beam/io/gcp/gcsfilesystem_test.py
@@ -382,8 +382,7 @@ class GCSFileSystemTest(unittest.TestCase):
def _verify_lineage(self, uri, expected_segments):
lineage_mock = mock.MagicMock()
self.fs.report_lineage(uri, lineage_mock)
- lineage_mock.add.assert_called_once_with(
- "gcs", *expected_segments, last_segment_sep='/')
+ lineage_mock.add.assert_called_once_with("gcs", *expected_segments)
if __name__ == '__main__':
diff --git a/sdks/python/apache_beam/io/localfilesystem.py
b/sdks/python/apache_beam/io/localfilesystem.py
index daf69b8d030..e9fe7dd4b1c 100644
--- a/sdks/python/apache_beam/io/localfilesystem.py
+++ b/sdks/python/apache_beam/io/localfilesystem.py
@@ -364,6 +364,3 @@ class LocalFileSystem(FileSystem):
if exceptions:
raise BeamIOError("Delete operation failed", exceptions)
-
- def report_lineage(self, path, lineage):
- lineage.add('filesystem', 'localhost', path, last_segment_sep='/')
diff --git a/sdks/python/apache_beam/metrics/cells.py
b/sdks/python/apache_beam/metrics/cells.py
index c2c2e8015ef..a75f2dc3168 100644
--- a/sdks/python/apache_beam/metrics/cells.py
+++ b/sdks/python/apache_beam/metrics/cells.py
@@ -811,9 +811,6 @@ class BoundedTrieData(object):
else:
return False
- def flattened(self):
- return self.as_trie().flattened()
-
def to_proto(self) -> metrics_pb2.BoundedTrie:
return metrics_pb2.BoundedTrie(
bound=self._bound,
@@ -825,9 +822,7 @@ class BoundedTrieData(object):
return BoundedTrieData(
bound=proto.bound,
singleton=tuple(proto.singleton) if proto.singleton else None,
- root=(
- _BoundedTrieNode.from_proto(proto.root)
- if proto.HasField('root') else None))
+ root=_BoundedTrieNode.from_proto(proto.root) if proto.root else None)
def as_trie(self):
if self._root is not None:
diff --git a/sdks/python/apache_beam/metrics/metric.py
b/sdks/python/apache_beam/metrics/metric.py
index 58a74afb9de..33af25e20ca 100644
--- a/sdks/python/apache_beam/metrics/metric.py
+++ b/sdks/python/apache_beam/metrics/metric.py
@@ -33,7 +33,6 @@ from typing import TYPE_CHECKING
from typing import Dict
from typing import FrozenSet
from typing import Iterable
-from typing import Iterator
from typing import List
from typing import Optional
from typing import Set
@@ -339,12 +338,12 @@ class Lineage:
for lineage tracking."""
LINEAGE_NAMESPACE = "lineage"
- SOURCE = "sources_v2"
- SINK = "sinks_v2"
+ SOURCE = "sources"
+ SINK = "sinks"
_METRICS = {
- SOURCE: Metrics.bounded_trie(LINEAGE_NAMESPACE, SOURCE),
- SINK: Metrics.bounded_trie(LINEAGE_NAMESPACE, SINK)
+ SOURCE: Metrics.string_set(LINEAGE_NAMESPACE, SOURCE),
+ SINK: Metrics.string_set(LINEAGE_NAMESPACE, SINK)
}
def __init__(self, label: str) -> None:
@@ -393,32 +392,8 @@ class Lineage:
return ':'.join((system, subtype, segs))
return ':'.join((system, segs))
- @staticmethod
- def _get_fqn_parts(
- system: str,
- *segments: str,
- subtype: Optional[str] = None,
- last_segment_sep: Optional[str] = None) -> Iterator[str]:
- yield system + ':'
- if subtype:
- yield subtype + ':'
- if segments:
- for segment in segments[:-1]:
- yield segment + '.'
- if last_segment_sep:
- sub_segments = segments[-1].split(last_segment_sep)
- for sub_segment in sub_segments[:-1]:
- yield sub_segment + last_segment_sep
- yield sub_segments[-1]
- else:
- yield segments[-1]
-
def add(
- self,
- system: str,
- *segments: str,
- subtype: Optional[str] = None,
- last_segment_sep: Optional[str] = None) -> None:
+ self, system: str, *segments: str, subtype: Optional[str] = None) ->
None:
"""
Adds the given details as Lineage.
@@ -439,35 +414,21 @@ class Lineage:
The first positional argument serves as system, if full segments are
provided, or the full FQN if it is provided as a single argument.
"""
- self.add_raw(
- *self._get_fqn_parts(
- system,
- *segments,
- subtype=subtype,
- last_segment_sep=last_segment_sep))
-
- def add_raw(self, *rollup_segments: str) -> None:
- """Adds the given fqn as lineage.
-
- `rollup_segments` should be an iterable of strings whose concatenation
- is a valid Dataplex FQN. In particular, this means they will often have
- trailing delimiters.
- """
- self.metric.add(rollup_segments)
+ system_or_details = system
+ if len(segments) == 0 and subtype is None:
+ self.metric.add(system_or_details)
+ else:
+ self.metric.add(self.get_fq_name(system, *segments, subtype=subtype))
@staticmethod
- def query(results: MetricResults,
- label: str,
- truncated_marker: str = '*') -> Set[str]:
+ def query(results: MetricResults, label: str) -> Set[str]:
if not label in Lineage._METRICS:
raise ValueError("Label {} does not exist for Lineage", label)
response = results.query(
MetricsFilter().with_namespace(Lineage.LINEAGE_NAMESPACE).with_name(
- label))[MetricResults.BOUNDED_TRIES]
+ label))[MetricResults.STRINGSETS]
result = set()
for metric in response:
- for fqn in metric.committed.flattened():
- result.add(''.join(fqn[:-1]) + (truncated_marker if fqn[-1] else ''))
- for fqn in metric.attempted.flattened():
- result.add(''.join(fqn[:-1]) + (truncated_marker if fqn[-1] else ''))
+ result.update(metric.committed)
+ result.update(metric.attempted)
return result
diff --git a/sdks/python/apache_beam/metrics/metric_test.py
b/sdks/python/apache_beam/metrics/metric_test.py
index 2e2e51b267a..524a2143172 100644
--- a/sdks/python/apache_beam/metrics/metric_test.py
+++ b/sdks/python/apache_beam/metrics/metric_test.py
@@ -271,19 +271,14 @@ class LineageTest(unittest.TestCase):
def test_add(self):
lineage = Lineage(Lineage.SOURCE)
- added = set()
+ stringset = set()
# override
- lineage.metric = added
+ lineage.metric = stringset
lineage.add("s", "1", "2")
lineage.add("s:3.4")
lineage.add("s", "5", "6.7")
lineage.add("s", "1", "2", subtype="t")
- lineage.add("sys", "seg1", "seg2", "seg3/part2/part3",
last_segment_sep='/')
- self.assertSetEqual(
- added,
- {('s:', '1.', '2'), ('s:3.4:', ), ('s:', '5.', '6.7'),
- ('s:', 't:', '1.', '2'),
- ('sys:', 'seg1.', 'seg2.', 'seg3/', 'part2/', 'part3')})
+ self.assertSetEqual(stringset, {"s:1.2", "s:3.4", "s:t:1.2", "s:5.`6.7`"})
if __name__ == '__main__':