SilverStreamingApplication 1.2__tar.gz → 1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- silverstreamingapplication-1.4/PKG-INFO +7 -0
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/SilverMerge.py +95 -11
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/WorkflowBuilder.py +26 -4
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/setup.py +2 -2
- silverstreamingapplication-1.4/silverstreamingapplication.egg-info/PKG-INFO +7 -0
- {SilverStreamingApplication-1.2/SilverStreamingApplication.egg-info → silverstreamingapplication-1.4/silverstreamingapplication.egg-info}/SOURCES.txt +5 -7
- SilverStreamingApplication-1.2/PKG-INFO +0 -12
- SilverStreamingApplication-1.2/SilverStreamingApplication.egg-info/PKG-INFO +0 -12
- SilverStreamingApplication-1.2/com/phida/main/__init__.py +0 -0
- SilverStreamingApplication-1.2/com/phida/tests/__init__.py +0 -0
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/Maintenance.py +0 -0
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/Operations.py +0 -0
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/PartitionAnalyzer.py +0 -0
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/PerfAnalyzer.py +0 -0
- {SilverStreamingApplication-1.2/com → silverstreamingapplication-1.4/com/phida/main}/__init__.py +0 -0
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/logging.py +0 -0
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/sparksession.py +0 -0
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/utils.py +0 -0
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/utilsDatabricksAPI.py +0 -0
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/tests/OperationsTest.py +0 -0
- {SilverStreamingApplication-1.2/com/phida → silverstreamingapplication-1.4/com/phida/tests}/__init__.py +0 -0
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/tests/main_test.py +0 -0
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/tests/utils_test.py +0 -0
- {SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/setup.cfg +0 -0
- {SilverStreamingApplication-1.2/SilverStreamingApplication.egg-info → silverstreamingapplication-1.4/silverstreamingapplication.egg-info}/dependency_links.txt +0 -0
- {SilverStreamingApplication-1.2/SilverStreamingApplication.egg-info → silverstreamingapplication-1.4/silverstreamingapplication.egg-info}/top_level.txt +0 -0
{SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/SilverMerge.py
RENAMED
|
@@ -2,10 +2,10 @@ from com.phida.main.sparksession import spark, logger
|
|
|
2
2
|
from com.phida.main.Operations import tableExists, addDerivedColumns, createDeltaTable, alterDeltaTable, \
|
|
3
3
|
dropColumns, getDerivedColumnsList, schemaDiff, getKeyCols, buildJoinCondition, buildColumnsDict, hiveDDL, schemaDataTypeDiff
|
|
4
4
|
from com.phida.main.utils import pathExists, convertStrToList
|
|
5
|
-
from pyspark.sql.functions import row_number, col, broadcast
|
|
5
|
+
from pyspark.sql.functions import row_number, col, broadcast,expr, max as spark_max
|
|
6
6
|
from pyspark.sql.window import Window
|
|
7
7
|
from delta.tables import DeltaTable
|
|
8
|
-
|
|
8
|
+
from pyspark.sql import DataFrame
|
|
9
9
|
|
|
10
10
|
class SilverMerge:
|
|
11
11
|
"""
|
|
@@ -24,6 +24,9 @@ class SilverMerge:
|
|
|
24
24
|
dropColumnStr: String - Columns to be dropped from source table df
|
|
25
25
|
pruneColumn: String - Column for applying the prune filter in the merge ON condition clause \
|
|
26
26
|
(to improve performance of the merge)
|
|
27
|
+
cluster_table_path: String - Path to the cluster table
|
|
28
|
+
enable_delete_filter: String - Flag to enable/disable filtering of deleted records (default: 'N')
|
|
29
|
+
use_auto_loader: String - Flag to enable/disable use of autoloader for reading source table (default: 'N')
|
|
27
30
|
|
|
28
31
|
methods:
|
|
29
32
|
silverCleansing
|
|
@@ -35,12 +38,12 @@ class SilverMerge:
|
|
|
35
38
|
from com.phida.SilverMerge import SilverMerge
|
|
36
39
|
silverMergeObj = SilverMerge(srcDatabaseName, srcTableName, tgtDatabaseName, tgtTableName,
|
|
37
40
|
tgtCheckpoint, tgtTablePath, tgtPartitionColumns, derivedColExpr,
|
|
38
|
-
triggerOnce, dropColumnStr, pruneColumn)
|
|
41
|
+
triggerOnce, dropColumnStr, pruneColumn, cluster_table_path, enable_delete_filter, use_auto_loader)
|
|
39
42
|
|
|
40
43
|
"""
|
|
41
44
|
def __init__(self, srcDatabaseName, srcTableName, tgtDatabaseName, tgtTableName,
|
|
42
45
|
tgtCheckpoint, tgtTablePath, tgtPartitionColumns, derivedColExpr,
|
|
43
|
-
triggerOnce, dropColumnStr, pruneColumn):
|
|
46
|
+
triggerOnce, dropColumnStr, pruneColumn, cluster_table_path, enable_delete_filter, use_auto_loader):
|
|
44
47
|
"""
|
|
45
48
|
desc:
|
|
46
49
|
Initialize the required class variables
|
|
@@ -57,7 +60,9 @@ class SilverMerge:
|
|
|
57
60
|
triggerOnce: String - Whether continuous streaming or just once
|
|
58
61
|
dropColumnStr: String - Columns to be dropped from source table df
|
|
59
62
|
pruneColumn: String - Column to use for dynamic file pruning (future feature)
|
|
60
|
-
|
|
63
|
+
cluster_table_path: String - Path to the cluster table
|
|
64
|
+
enable_delete_filter: String - Flag to enable/disable filtering of deleted records (default: 'N')
|
|
65
|
+
use_auto_loader: String - Flag to enable/disable use of autoloader for reading source table (default: 'N')
|
|
61
66
|
"""
|
|
62
67
|
|
|
63
68
|
logger.info("phida_log: Initialising class variables")
|
|
@@ -73,6 +78,9 @@ class SilverMerge:
|
|
|
73
78
|
self.triggerOnce = triggerOnce
|
|
74
79
|
self.dropColumnStr = dropColumnStr
|
|
75
80
|
self.pruneColumn = pruneColumn
|
|
81
|
+
self.cluster_table_path = cluster_table_path
|
|
82
|
+
self.enable_delete_filter = enable_delete_filter.strip().upper() if enable_delete_filter else 'N'
|
|
83
|
+
self.use_auto_loader = use_auto_loader.strip().upper() if use_auto_loader and use_auto_loader.strip().upper() == 'Y' else 'N'
|
|
76
84
|
|
|
77
85
|
logger.info(f"phida_log: Check if source table {self.srcDatabaseName}.{self.srcTableName} exists")
|
|
78
86
|
|
|
@@ -82,7 +90,11 @@ class SilverMerge:
|
|
|
82
90
|
|
|
83
91
|
logger.info(f"phida_log: initialising derived class variables")
|
|
84
92
|
|
|
85
|
-
|
|
93
|
+
if self.use_auto_loader == 'N':
|
|
94
|
+
self.srcDF = spark.readStream.table(self.srcDatabaseName + "." + self.srcTableName)
|
|
95
|
+
elif self.use_auto_loader == 'Y':
|
|
96
|
+
src_table_path = spark.sql(f"DESCRIBE DETAIL {self.srcDatabaseName}.{self.srcTableName}").collect()[0]['location']
|
|
97
|
+
self.srcDF = spark.readStream.format("cloudFiles").option("cloudFiles.format", "parquet").option("cloudFiles.schemaLocation", src_table_path + "/_cloudfiles/_schemas").load(src_table_path)
|
|
86
98
|
|
|
87
99
|
self.keyCols = getKeyCols(self.srcDatabaseName, self.srcTableName)
|
|
88
100
|
|
|
@@ -203,6 +215,64 @@ class SilverMerge:
|
|
|
203
215
|
else:
|
|
204
216
|
logger.info(f"phida_log: There is no change in schema in silver")
|
|
205
217
|
|
|
218
|
+
def filter_deleted_records(self, microBatchOutputDF):
|
|
219
|
+
"""
|
|
220
|
+
Filters out records from microBatchOutputDF that have been marked as deleted in the Bronze table
|
|
221
|
+
within the last 4 hours based on hvr_integ_key.
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
if microBatchOutputDF.isEmpty():
|
|
225
|
+
return microBatchOutputDF
|
|
226
|
+
|
|
227
|
+
filtered_table_name = f"{self.srcDatabaseName}.{self.srcTableName}_filtered"
|
|
228
|
+
|
|
229
|
+
if not tableExists(self.srcDatabaseName, f"{self.srcTableName}_filtered"):
|
|
230
|
+
return microBatchOutputDF
|
|
231
|
+
|
|
232
|
+
delete_records = spark.read.table(filtered_table_name).filter(col("source_operation") == 0)
|
|
233
|
+
|
|
234
|
+
if delete_records.isEmpty():
|
|
235
|
+
return microBatchOutputDF
|
|
236
|
+
|
|
237
|
+
delete_keys_df = (delete_records.groupBy(self.keyColsList).agg(spark_max(col("hvr_integ_key")).alias("delete_hvr_integ_key")))
|
|
238
|
+
|
|
239
|
+
joined_df = microBatchOutputDF.join(broadcast(delete_keys_df), on=self.keyColsList, how="left")
|
|
240
|
+
|
|
241
|
+
filtered_df = (joined_df.filter(
|
|
242
|
+
(col("delete_hvr_integ_key").isNull()) |
|
|
243
|
+
(col("hvr_integ_key") > col("delete_hvr_integ_key"))
|
|
244
|
+
)
|
|
245
|
+
.drop("delete_hvr_integ_key"))
|
|
246
|
+
|
|
247
|
+
return filtered_df
|
|
248
|
+
|
|
249
|
+
def preprocess_dataframe(self, df: DataFrame, parent_keys: list) -> DataFrame:
|
|
250
|
+
|
|
251
|
+
"""Removes rows where all or any 'source_operation' values for a group are 8."""
|
|
252
|
+
|
|
253
|
+
grouped_df = df.groupBy(parent_keys).agg(
|
|
254
|
+
expr("bool_and(source_operation = 8)").alias("all_8"),
|
|
255
|
+
expr("bool_or(source_operation = 8)").alias("any_8")
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
return df.join(grouped_df, on=parent_keys, how="left").filter("NOT (all_8 OR any_8)")
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def get_parent_keys(self, df: DataFrame, kernel_value: str, unpack_table_value: str) -> list:
|
|
262
|
+
|
|
263
|
+
"""Get the parent keys for the given kernel and unpack_table values"""
|
|
264
|
+
|
|
265
|
+
filtered_df = df.filter(
|
|
266
|
+
(col("kernel") == kernel_value) & (col("unpack_table") == unpack_table_value)
|
|
267
|
+
)
|
|
268
|
+
row = filtered_df.select("key_columns_for_pack_table", "key_columns_for_unpack_table").first()
|
|
269
|
+
pack_keys = set(row["key_columns_for_pack_table"].split(',')) if row["key_columns_for_pack_table"] else set()
|
|
270
|
+
unpack_keys = set(row["key_columns_for_unpack_table"].split(',')) if row["key_columns_for_unpack_table"] else set()
|
|
271
|
+
parent_keys = list(pack_keys & unpack_keys)
|
|
272
|
+
return parent_keys
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
|
|
206
276
|
def upsertToDelta(self, microBatchOutputDF, batchId):
|
|
207
277
|
"""
|
|
208
278
|
desc:
|
|
@@ -221,16 +291,30 @@ class SilverMerge:
|
|
|
221
291
|
tip:
|
|
222
292
|
N/A
|
|
223
293
|
"""
|
|
224
|
-
|
|
294
|
+
op_8_df = microBatchOutputDF.filter("source_operation = 8")
|
|
225
295
|
microBatchOutputDF = microBatchOutputDF.filter("source_operation in (0,1,2,3)")
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
296
|
+
microBatchOutputDF_8 = microBatchOutputDF.filter("source_operation in (0,1,2,3,8)")
|
|
297
|
+
tgtDeltaTable = DeltaTable.forName(spark, self.tgtDatabaseName + "." + self.tgtTableName)
|
|
298
|
+
|
|
299
|
+
if self.cluster_table_path:
|
|
300
|
+
if not op_8_df.isEmpty():
|
|
301
|
+
table_details = DeltaTable.forName(spark, self.cluster_table_path)
|
|
302
|
+
table_details_df = table_details.toDF()
|
|
303
|
+
parent_keys = self.get_parent_keys(table_details_df, self.srcDatabaseName, self.srcTableName)
|
|
304
|
+
parent_key_conditions = " AND ".join([f"t.{col} = s.{col}" for col in parent_keys])
|
|
305
|
+
if not tgtDeltaTable.toDF().isEmpty():
|
|
306
|
+
tgtDeltaTable.alias("t").merge(microBatchOutputDF_8.alias("s"), parent_key_conditions) \
|
|
307
|
+
.whenMatchedDelete() \
|
|
308
|
+
.execute()
|
|
309
|
+
microBatchOutputDF = self.preprocess_dataframe(microBatchOutputDF_8, parent_keys)
|
|
310
|
+
|
|
311
|
+
windowSpec = Window.partitionBy(self.keyColsList).orderBy(col("hvr_integ_key").desc())
|
|
229
312
|
|
|
230
313
|
microBatchOutputDF = microBatchOutputDF.withColumn("latest_record", row_number().over(windowSpec)).filter(
|
|
231
314
|
"latest_record == 1").drop("latest_record")
|
|
232
315
|
|
|
233
|
-
|
|
316
|
+
if self.enable_delete_filter == 'Y':
|
|
317
|
+
microBatchOutputDF = self.filter_deleted_records(microBatchOutputDF)
|
|
234
318
|
|
|
235
319
|
tgtDeltaTable.alias("t").merge(microBatchOutputDF.alias("s"), self.condition) \
|
|
236
320
|
.whenMatchedDelete("s.source_operation in (0,3)") \
|
{SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/WorkflowBuilder.py
RENAMED
|
@@ -72,7 +72,7 @@ class WorkflowBuilder:
|
|
|
72
72
|
@staticmethod
|
|
73
73
|
def createTask(taskKey, jobClusterKey, notebookPath, srcDatabaseName, srcTableName, tgtDatabaseName, tgtTableName,
|
|
74
74
|
tgtCheckpoint, tgtTablePath, tgtPartitionColumns, derivedColExpr, triggerOnce, dropColumnStr,
|
|
75
|
-
pruneColumn,
|
|
75
|
+
pruneColumn, cluster_table_path, enable_delete_filter, enable_autoloader, library):
|
|
76
76
|
"""
|
|
77
77
|
desc:
|
|
78
78
|
A method for creating the dict/json for a single task
|
|
@@ -93,6 +93,10 @@ class WorkflowBuilder:
|
|
|
93
93
|
dropColumnStr: String - Columns to be dropped from source table df
|
|
94
94
|
pruneColumn: String - Column for applying the prune filter in the merge ON condition clause \
|
|
95
95
|
(to improve performance of the merge)
|
|
96
|
+
cluster_table_path: String - The path of the table in the cluster
|
|
97
|
+
enable_delete_filter: String - Flag to enable/disable filtering of deleted records (default: 'N')
|
|
98
|
+
enable_autoloader: String - Flag to enable/disable autoloader (default: 'N')
|
|
99
|
+
pythonWhlPath: String - Path of the python wheel file
|
|
96
100
|
library: String - Library to be installed for the job (with version number)
|
|
97
101
|
|
|
98
102
|
return:
|
|
@@ -130,7 +134,10 @@ class WorkflowBuilder:
|
|
|
130
134
|
"derived_col_expr": derivedColExpr,
|
|
131
135
|
"trigger_once": triggerOnce,
|
|
132
136
|
"prune_column": pruneColumn,
|
|
133
|
-
"drop_column_list": dropColumnStr
|
|
137
|
+
"drop_column_list": dropColumnStr,
|
|
138
|
+
"cluster_table_path": cluster_table_path,
|
|
139
|
+
"enable_delete_filter": str(enable_delete_filter),
|
|
140
|
+
"enable_autoloader": enable_autoloader
|
|
134
141
|
}
|
|
135
142
|
},
|
|
136
143
|
"timeout_seconds": timeout_seconds,
|
|
@@ -311,7 +318,7 @@ class WorkflowBuilder:
|
|
|
311
318
|
tgtTablePath = task['tgt_table_path']
|
|
312
319
|
triggerOnce = task['trigger_once']
|
|
313
320
|
dropColumnStr = task['drop_column_list']
|
|
314
|
-
|
|
321
|
+
|
|
315
322
|
try:
|
|
316
323
|
tgtPartitionColumns = task['tgt_partition_columns']
|
|
317
324
|
except:
|
|
@@ -326,11 +333,26 @@ class WorkflowBuilder:
|
|
|
326
333
|
pruneColumn = task['prune_column']
|
|
327
334
|
except:
|
|
328
335
|
pruneColumn = ""
|
|
336
|
+
|
|
337
|
+
try:
|
|
338
|
+
cluster_table_path = task['cluster_table_path']
|
|
339
|
+
except:
|
|
340
|
+
cluster_table_path = ""
|
|
341
|
+
|
|
342
|
+
try:
|
|
343
|
+
enable_delete_filter = task['enable_delete_filter']
|
|
344
|
+
except:
|
|
345
|
+
enable_delete_filter = ""
|
|
346
|
+
|
|
347
|
+
try:
|
|
348
|
+
enable_autoloader = task['enable_autoloader']
|
|
349
|
+
except:
|
|
350
|
+
enable_autoloader = ""
|
|
329
351
|
|
|
330
352
|
taskTemp = self.createTask(taskKey, jobClusterKey, notebookPath, srcDatabaseName, srcTableName,
|
|
331
353
|
tgtDatabaseName, tgtTableName, tgtCheckpoint, tgtTablePath,
|
|
332
354
|
tgtPartitionColumns, derivedColExpr, triggerOnce, dropColumnStr,
|
|
333
|
-
pruneColumn,
|
|
355
|
+
pruneColumn, cluster_table_path, enable_delete_filter, enable_autoloader, self.library)
|
|
334
356
|
|
|
335
357
|
tasks.append(taskTemp)
|
|
336
358
|
if jobId is None:
|
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
setup.py
|
|
2
|
-
SilverStreamingApplication.egg-info/PKG-INFO
|
|
3
|
-
SilverStreamingApplication.egg-info/SOURCES.txt
|
|
4
|
-
SilverStreamingApplication.egg-info/dependency_links.txt
|
|
5
|
-
SilverStreamingApplication.egg-info/top_level.txt
|
|
6
|
-
com/__init__.py
|
|
7
|
-
com/phida/__init__.py
|
|
8
2
|
com/phida/main/Maintenance.py
|
|
9
3
|
com/phida/main/Operations.py
|
|
10
4
|
com/phida/main/PartitionAnalyzer.py
|
|
@@ -19,4 +13,8 @@ com/phida/main/utilsDatabricksAPI.py
|
|
|
19
13
|
com/phida/tests/OperationsTest.py
|
|
20
14
|
com/phida/tests/__init__.py
|
|
21
15
|
com/phida/tests/main_test.py
|
|
22
|
-
com/phida/tests/utils_test.py
|
|
16
|
+
com/phida/tests/utils_test.py
|
|
17
|
+
silverstreamingapplication.egg-info/PKG-INFO
|
|
18
|
+
silverstreamingapplication.egg-info/SOURCES.txt
|
|
19
|
+
silverstreamingapplication.egg-info/dependency_links.txt
|
|
20
|
+
silverstreamingapplication.egg-info/top_level.txt
|
|
File without changes
|
|
File without changes
|
{SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/Maintenance.py
RENAMED
|
File without changes
|
{SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/Operations.py
RENAMED
|
File without changes
|
|
File without changes
|
{SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/PerfAnalyzer.py
RENAMED
|
File without changes
|
{SilverStreamingApplication-1.2/com → silverstreamingapplication-1.4/com/phida/main}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/main/sparksession.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/tests/OperationsTest.py
RENAMED
|
File without changes
|
|
File without changes
|
{SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/tests/main_test.py
RENAMED
|
File without changes
|
{SilverStreamingApplication-1.2 → silverstreamingapplication-1.4}/com/phida/tests/utils_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|