I'm running a group by query on the 1.8TB dataset ...
# general
b
I'm running a group by query on the 1.8TB dataset I mentioned above. The ray job is failing because some RaySwordfishActors are running out of memory, either during the repartition or write parquet step (it looks like they're interleaved, so it's hard to tell). The log I'm seeing is:
(raylet) node_manager.cc:3193: 1 Workers (tasks / actors) killed due to memory pressure (OOM)
I've tried various k8s pod sizing - currently I have 80 worker pods with 8cpu / 32gb. Does anyone know why so much memory would be used, or how to tune / think about queries like this?
āœ… 1
The form of the query is similar to the one above:
Copy code
df = daft.read_parquet(
  "<abfs://my_container@my_service_account.dfs.core.windows.net/in_table_path/>"
)
df = df.select(col("group_col"), col("other_col"))
  .groupby("group_col")
  .agg_list(col("other_col"))
df.write_parquet("<abfs://my_container@my_service_account.dfs.core.windows.net/out_table_path/>")
d
Hmm this is not ideal, but I wonder if it'll help to do this in two stages: 1. Write out partitioned data 2. Read partitioned data with agg list So something along the lines of
Copy code
import daft
from daft import col

df = daft.read_parquet(
    "<abfs://my_container@my_service_account.dfs.core.windows.net/in_table_path/>"
)

df = df.select(col("group_col"), col("other_col"))

df.write_parquet(
    "<abfs://my_container@my_service_account.dfs.core.windows.net/tmp_grouped/>",
    partition_cols=["group_col"],
)

# Stage 2

df = daft.read_parquet(
    "<abfs://my_container@my_service_account.dfs.core.windows.net/tmp_grouped/>",
    hive_partitioning=True,
)

df = df.groupby("group_col").agg_list(col("other_col"))

df.write_parquet(
    "<abfs://my_container@my_service_account.dfs.core.windows.net/out_table_path/>"
)
b
I can give that a try, but does that mean it's necessary to break up all queries needing a shuffle of this much data?
d
I think it's more the nature of an agg_list. You would need to keep the entire dataset in-memory/spill to disk until all rows have been shuffled
b
I do see that some spilling to disk is happening, since I see logs like
(raylet, ip=10.224.20.85) Spilled 8674 MiB, 866 objects, write throughput 1621 MiB/s
, but not enough to keep the actor from getting killed
So weirdly my write tasks aren't completing for that intermediate write
PhysicalScan->PhysicalWrite 0:   0%|          | 0.00/49.5k
Any ideas what could be happening there, or suggestions on how to debug? The tasks all eventually get queued up but none complete
It has something to do with
partition_cols
since when I remove that it writes fine
😮 1
d
That's really odd... just to confirm, we have permissions to create new folders in abfs?
b
yeah we do - it wrote out the tmp dir files fine without
partition_cols
I think I might see the issue - when I tried this locally I saw that
write_parquet
was writing out a partition dir for each unique
group_col
value. In my case the set of group values has very high cardinality, so this would lead to a very large number of output files.
I will try adding a derived partition column
adding the partition column was fast but it was still prohibitively slow to write out the partitions via
write_parquet
. However, increasing the number of partitions to 2000 via
df = df.repartition(2000, col("group_col"))
worked! I guess that makes sense - if this group by operation can't be spilled to disk, then a ray worker node needs to have the entire partition in memory, and so increasing the number of partitions will decrease peak memory usage. Am I understanding that correctly?
d
Ah cool, awesome debugging! Yup so if we broke it down fully: • With
agg_list
a specific
group_col
needs to be fully aggregated before it can be flushed from memory. With the initial dataset, we don't know when we've seen all values belonging to a specific
group_col
until we've shuffled all the data • By adding a partitioning step, all values belonging to a specific
group_col
now belong on the same worker on the same partition. So when we finally do the
agg_list
, on each worker, at most one partition needs to be materialized in-memory before we know that the `agg_list`s for belonging to that partition are completed. And whenever a batch of `agg_list`s is completed, it goes on to the next operator which is a write so it's removed from memory • Also, your idea to do an in-memory repartitioning was a great choice! I should've led with that instead of suggesting to write out the data šŸ˜…
šŸ™Œ 1
b
Thanks for breaking that down - it makes sense!
šŸ™Œ 1