From user-return-823-archive-asf-public=cust-asf.ponee.io@impala.apache.org Fri May 4 02:50:37 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id E3CD5180625 for ; Fri, 4 May 2018 02:50:33 +0200 (CEST) Received: (qmail 96247 invoked by uid 500); 4 May 2018 00:50:32 -0000 Mailing-List: contact user-help@impala.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: user@impala.apache.org Delivered-To: mailing list user@impala.apache.org Received: (qmail 96233 invoked by uid 99); 4 May 2018 00:50:32 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd2-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 04 May 2018 00:50:32 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd2-us-west.apache.org (ASF Mail Server at spamd2-us-west.apache.org) with ESMTP id 3515A1A0225 for ; Fri, 4 May 2018 00:50:32 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd2-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.88 X-Spam-Level: * X-Spam-Status: No, score=1.88 tagged_above=-999 required=6.31 tests=[DC_PNG_UNO_LARGO=0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, HTML_MESSAGE=2, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_PASS=-0.001] autolearn=disabled Authentication-Results: spamd2-us-west.apache.org (amavisd-new); dkim=pass (2048-bit key) header.d=googlemail.com Received: from mx1-lw-us.apache.org ([10.40.0.8]) by localhost (spamd2-us-west.apache.org [10.40.0.9]) (amavisd-new, port 10024) with ESMTP id OHku5-wvhFzE for ; Fri, 4 May 2018 00:50:21 +0000 (UTC) Received: from mail-wm0-f43.google.com (mail-wm0-f43.google.com [74.125.82.43]) by mx1-lw-us.apache.org (ASF Mail Server at mx1-lw-us.apache.org) with ESMTPS id 752E05F5DF for ; Fri, 4 May 2018 00:50:19 +0000 (UTC) Received: by mail-wm0-f43.google.com with SMTP id m198-v6so1113696wmg.3 for ; Thu, 03 May 2018 17:50:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20161025; h=subject:from:to:references:message-id:date:user-agent:mime-version :in-reply-to; bh=ewhlknP/gz7LbEHlYF71GE33BylOVfqDKWil03beEcs=; b=UI/67/uXFTeIcrw0DdrN2sdBoxGPx7+hZzbPEJBbOwUE84P4fW4gLsY+bz9eZcX0Yj ddvIIp4gbH4SU9B7ahodM8eLfrtPGpHSzH1j6IhWY+0em+OsdD58oAhIoaB0w5+BYchi wjjzFfsZ9uhIO+jMZhJJMJPVw+nQJRGUDEHkq9X+iT73cVYxFx0Giqyu8JJdvglFvVFb 4FprU2S9KWBSpnn8tyPqIoiwtN/eZc1dkkzWNaSm++alyhTWvyNLnoOjtl7wNDH72IeC OLFzUNuaISookhs3JPJyJybG75de6JP+BzjTDakiTGH3ote7u2dIFjYavBZfiJoJSKJJ iPuA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:from:to:references:message-id:date :user-agent:mime-version:in-reply-to; bh=ewhlknP/gz7LbEHlYF71GE33BylOVfqDKWil03beEcs=; b=dbOEKYVR9rMwiEuXpna/r4F3Dm3EIcs2ysijlR0PfePrHglkSY76D809AZI4dMSGFH wyJIgOnN6Ydm5X7BBUZQUCFKuPZIBulM3eEsWa8czEF5QYp0DZFTtuV2RfhcVZHYn025 47wVaLdkamBqaeKJu6GmLftouU+Ifm9GBK0A+GM4ICY9/5Q/abEGpTzhL3nnoq834wRN P9eCdnBmyoIOpsVSltLBDSAfZraUdJJ0LwF8NMepyaHWbMHZTjOaunXJwYZMd8wdEhC4 LLL+zBk92V5sCa4W20HB0azO6vEi/Vk4G/xMkuy0pINk3UalFWmxcRWOkxzJbLqaR0jt vO2Q== X-Gm-Message-State: ALQs6tDBBhKuJNNWbEiPbfH4cKe7X/qqaKGxffGcOj6CbsyA50yzaW5o wNAk0X/SHCQMAELj X-Google-Smtp-Source: AB8JxZpDjOp+9r+doyAzgplyN81+m8Ar6Xx2ye+ODotvAfWgmHqkDopUm88H4an5Kmcv/FdD/H5tEA== X-Received: by 10.28.139.136 with SMTP id n130mr15559933wmd.8.1525395017968; Thu, 03 May 2018 17:50:17 -0700 (PDT) Received: from [10.0.0.2] (p5DCC8791.dip0.t-ipconnect.de. [93.204.135.145]) by smtp.googlemail.com with ESMTPSA id 187sm924212wmu.41.2018.05.03.17.50.15 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 03 May 2018 17:50:16 -0700 (PDT) Subject: Re: Local join instead of data exchange - co-located blocks From: Philipp Krause To: user@impala.apache.org References: <1e200a35-8e6f-e5be-5895-a7a9d9199b69@googlemail.com> <68e0449e-4598-f049-b465-9ada5044a26e@googlemail.com> <2ff50473-377d-3b01-8f32-2a6bfb08644b@googlemail.com> Message-ID: <59c9d27e-dfef-3635-2cd7-b713be00dbc9@googlemail.com> Date: Fri, 4 May 2018 02:51:47 +0200 User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:52.0) Gecko/20100101 Thunderbird/52.7.0 MIME-Version: 1.0 In-Reply-To: Content-Type: multipart/mixed; boundary="------------CB415E39653E950D398ABE97" X-AVK-Virus-Check: AVA 25.16904;1239E1D X-AVK-Spam-Check: 1;str=0001.0A0B0203.5AEBAE48.002E,ss=1,re=0.000,recu=0.000,reip=0.000,cl=1,cld=1,fgs=0;F1206 This is a multi-part message in MIME format. --------------CB415E39653E950D398ABE97 Content-Type: multipart/alternative; boundary="------------6127C242AA748F22A9016C4D" --------------6127C242AA748F22A9016C4D Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit Hello Alex, I have tried out several configurations but I still couldn't find a solution for my problem :( In the query summary (s. attachment) it looks like as if no rows are read. Do you have an idea what I have to change? I am sorry for the circumstances and thank you once more for the great support to get this working! Am 29.04.2018 um 21:21 schrieb Philipp Krause: > > Hi Alex, > I got the modified version working on my cluster. The query plan looks > exactly as wanted (s. attachment). This is awesome! Unfortunately the > result set is empty. As you can see in query_state.png, the scan > progress always shows 50% although the query has finished. > > The only modification in the code is the if statement you pointed to > me (I set it to true). Maybe I have to give Impala the information > about the lhs / rhs join partition since there are no exchange nodes > now (like in the following lines)? The corresponding  partitions / > blocks of each table are on the same node. > > I think we are very close to the final result and I hope you can help > me once more. Thank you so much! > > Best regards > Philipp > > > Am 24.04.2018 um 18:00 schrieb Alexander Behm: >> On Tue, Apr 24, 2018 at 5:31 AM, Philipp Krause >> > > wrote: >> >> To prevent the broadcast join I could simply use the shuffle >> operator in the query: >> >> SELECT * FROM business_partition_1 INNER JOIN [SHUFFLE] >> business_partition_2 WHERE >> business_partition_1.businessid=business_partition_2.businessid >> >> >> Not sure what version of Impala you are using, and whether hints >> override any changes you might make. I suggest you make the code work >> as you wish without requiring hints. >> >> >> I think the broadcast is currently only used because of my very >> small test tables. >> >> This gives me the plan attached as partitioned_shuffle.png. Since >> my modified version isn't working yet, I partitioned both tables >> on businessid in Impala. The "hack" should only help to get into >> the if-condition if I partition the data manually, right?. But in >> this case (if the partitioning is done by Impala itself) Impala >> should get into the if-condition anyway. Unfortunately I can't >> see a difference in the plan compared to my unpartitioned tables >> (unpartitioned.png) concerning the exchange nodes. My goal is >> actually to get rid of all exchange nodes since the corresponding >> data is already present on each node. Actually the plan should >> look the same except for the 03 and 04 exchange then. >> >> >> I understand the picture and goal. I suggest you read, understand, >> and modify the code in DistributedPlanner.createHashJoinFragment() to >> create the plan shape that you want. >> I don't know how you are producing these plans. Are you sure your >> code changes are taking effect? >> >> >> Are there other changes neccessary to just take >> Table1-partition/block X and Table2-partition/block Y on each >> node and join them without any data exchange? Actually each node >> should take all its local blocks for both tables, join them and >> pass the results back to the coordinator where all results come >> together (please see explanation.png). >> >> >> No. You just need to create the plan without exchange nodes, as we've >> already gone through early in this thread. >> >> >> I'm looking forward hearing from you. I hope we can realise this. >> >> Best regards >> Philipp >> >> >> Am 24.04.2018 um 07:03 schrieb Alexander Behm: >>> On Mon, Apr 23, 2018 at 7:24 PM, Philipp Krause >>> >> > wrote: >>> >>> Hi Alex, >>> thanks for the information! I've compiled the >>> cdh5.13.1-release and replaced >>> impala-frontend-0.1-SNAPSHOT.jar (which seems to include the >>> changes in the DistributedPlanner.java). There still seems >>> to to be a method missing after replacing the jar but I'll >>> try to figure that out. >>> >>> I have two questions concerning the code fragment in the >>> DistributedPlanner.java you pointed to me. >>> >>> First: >>> The attached graphic shows the query plan for two tables >>> which are partitioned on the join attribute (standard impala >>> version without any changes). If I change the if-condition >>> to true for my modified version I expect to get the same >>> result for my "hand made" partitions (outside of impala). >>> But why is there an exchange broadcast (even in the standard >>> version)? I mean, if I have a partition of Table 1 with ID=0 >>> on Node X why is there a broadcast of the partition of Table >>> 2 with ID=0 on Node Y? Actually this partition only has to >>> be sent to Node X where the matching partition of Table 1 is >>> located (instead of sending it to all nodes (broadcast)). Or >>> is this exactly the case and it's only shown as a broadcast >>> here in the graphics? >>> >>> >>> You are reading the plan correctly. Impala simply does not >>> implement that optimization. >>> >>> >>> Second: >>> All corresponding blocks of the partitioned tables are on >>> the same node (e.g. Table 1 Partition with ID=0, Table 2 >>> Partition with ID=0 => Node 1 etc.). This is what I did >>> manually. As already mentioned before I want to join these >>> partitions (blocks) locally on each node. But if I'm >>> correct, the modification in the DistributedPlanner will >>> also only lead to the plan in the attached graphic so that >>> no further exchanges are created if I use my "hand made" >>> partitions. But there is still the broadcast exchange which >>> distributes the partitions across the cluster which isn't >>> neccessary because all needed blocks are already on the same >>> node and are ready to get joined locally. Is there a way to >>> realise that and get rid of the broadcast exchange? >>> >>> You are right. That hack in DistributedPlanner only works for >>> partitioned hash joins. You can probably stitch the plan >>> together at an earlier place, e.g.: >>> https://github.com/apache/impala/blob/master/fe/src/main/java/org/apache/impala/planner/DistributedPlanner.java#L506 >>> >>> >>> Please correct me if I'm wrong with my assumptions. >>> >>> Thank you very much! >>> >>> Best regards >>> Philipp >>> >>> >>> >>> >>> >>> >>> >>> Am 18.04.2018 um 07:16 schrieb Alexander Behm: >>>> Your CM-managed cluster must be running a "compatible" >>>> Impala version already for this trick to work. It looks >>>> like your catalog binary is trying to find a method which >>>> does not exist in the .jar, presumably because your .jar is >>>> built based on a different version of Impala where that >>>> method does not exist anymore. >>>> >>>> It looks like you have CDH 5.13.3 installed. CDH 5.13.3 is >>>> based on Impala 2.10, see: >>>> https://www.cloudera.com/documentation/enterprise/release-notes/topics/cdh_vd_cdh_package_tarball_513.html#cm_vd_cdh_package_tarball_513 >>>> >>>> >>>> That means this binary copying trick will only work with a >>>> modified version of Impala 2.10, and very likely will not >>>> work with a different version. >>>> >>>> It's probably easier to test with the mini cluster first. >>>> Alternatively, it "might" work if you replace all the >>>> binaries mentioned above, but it's quite possible that will >>>> not work. >>>> >>>> On Sun, Apr 15, 2018 at 7:13 PM, Philipp Krause >>>> >>> > wrote: >>>> >>>> Hi Alex! Thank you for the list! The build of the >>>> modified cdh5-trunk branch (debug mode) was sucessfull. >>>> After replacing "impala-frontend-0.1-SNAPSHOT.jar" in >>>> /opt/cloudera/parcels/CDH-5.13.1-1.cdh5.13.1.p0.2/jars/ >>>> I got the following error in my existing cluster: >>>> F0416 01:16:45.402997 17897 catalog.cc:69] >>>> NoSuchMethodError: getCatalogObjects >>>> When I switch back to the original jar file the error >>>> is gone. So it must be something wrong with this file I >>>> guess. But I wonder about the error in catalog.cc >>>> because I didn't touch any .cc files. >>>> >>>> I also replaced >>>> "impala-data-source-api-1.0-SNAPSHOT.jar". The other >>>> jar files do not exist in my impala installation >>>> (CDH-5.13.1). >>>> >>>> What am I doing wrong? >>>> >>>> Best regards >>>> Philipp >>>> >>>> >>>> Am 13.04.2018 um 20:12 schrieb Alexander Behm: >>>>> Here's the foll list. It might not be minimal, but >>>>> copying/overwriting these should work. >>>>> >>>>> debug/service/impalad >>>>> debug/service/libfesupport.so >>>>> debug/service/libService.a >>>>> release/service/impalad >>>>> release/service/libfesupport.so >>>>> release/service/libService.a >>>>> yarn-extras-0.1-SNAPSHOT.jar >>>>> impala-data-source-api-1.0-SNAPSHOT-sources.jar >>>>> impala-data-source-api-1.0-SNAPSHOT.jar >>>>> impala-frontend-0.1-SNAPSHOT-tests.jar >>>>> impala-frontend-0.1-SNAPSHOT.jar >>>>> libkudu_client.so.0.1.0 >>>>> libstdc++.so.6.0.20 >>>>> impala-no-sse.bc >>>>> impala-sse.bc >>>>> libimpalalzo.so >>>>> >>>>> If you are only modifying the Java portion (like >>>>> DistributedPlanner), then only copying/replacing the >>>>> *.jar files should be sufficient. >>>>> >>>>> On Fri, Apr 13, 2018 at 11:00 AM, Philipp Krause >>>>> >>>> > wrote: >>>>> >>>>> Yes, I have a running (virtual) cluster. I would >>>>> try to follow your way with the custom impala >>>>> build (DistributedPlanner.java is the only >>>>> modified file at the moment). Thank you in advance >>>>> for the file list! >>>>> >>>>> Best regards >>>>> Philipp >>>>> >>>>> Alexander Behm >>>> > schrieb am Fr., >>>>> 13. Apr. 2018, 18:45: >>>>> >>>>> I'm not really following your >>>>> installation/setup and am not an expert on >>>>> Cloudera Manager installation/config. If you >>>>> are going to build Impala anyway, it's >>>>> probably easiest to test on Impala's >>>>> minicluster first. >>>>> >>>>> In general, if you have a running Cloudera >>>>> Managed cluster, you can deploy a custom >>>>> Impala build by simply overwriting the Impala >>>>> existing binaries and jars with the new build. >>>>> If you want to go this route, I can give you a >>>>> full list of files you need to replace. >>>>> >>>>> On Tue, Apr 10, 2018 at 11:44 AM, Philipp >>>>> Krause >>>> > wrote: >>>>> >>>>> Thank you for the explanation! Yes, I'm >>>>> using HDFS. The single replica setup is >>>>> only for test purposes at the moment. I >>>>> think this makes it easier to gain some >>>>> first results since less modifications >>>>> (scheduler etc.) are neccessary. >>>>> I would like to test the >>>>> DistributedPlanner modification in my >>>>> virtual cluster. I used a customized >>>>> Vagrant script to install Impala on >>>>> multiple hosts (s.attachment). It simply >>>>> installs cloudera-manager-server-db, >>>>> cloudera-manager-server and >>>>> cloudera-manager-daemons via apt-get. What >>>>> would be the simplest solution to setup my >>>>> modified version? Could I simply call >>>>> ./buildall.sh and change the script to >>>>> sth. like this? >>>>> >>>>> echo "Install java..." >>>>> apt-get -q -y --force-yes install >>>>> oracle-j2sdk1.7 >>>>> echo "Download impala..." >>>>> wget https://... where I uploaded my >>>>> modified version >>>>> echo "Extract impala..." >>>>> tar -xvzf Impala-cdh5-trunk.tar.gz >>>>> cd Impala-cdh5-trunk >>>>> echo "Build impala..." >>>>> ./buildall.sh >>>>> echo "Start impala instances..." >>>>> service cloudera-scm-server-db initdb >>>>> service cloudera-scm-server-db start >>>>> service cloudera-scm-server start >>>>> >>>>> Or is there another, maybe even easier >>>>> method, to test the code? Maybe via >>>>> bootstrap_development.sh / minicluster? >>>>> >>>>> Best regards >>>>> Philipp >>>>> >>>>> 2018-04-05 18:39 GMT+02:00 Alexander Behm >>>>> >>>> >: >>>>> >>>>> Apologies for the late response. Btw, >>>>> your previous post was clear enough to >>>>> me, so no worries :) >>>>> >>>>> >>>>> On Wed, Apr 4, 2018 at 7:46 AM, >>>>> Philipp Krause >>>>> >>>> > >>>>> wrote: >>>>> >>>>> Hello Alex, >>>>> >>>>> I think my previous post has been >>>>> too long and confusing. I >>>>> apologize for that! >>>>> >>>>> If replicas are completely >>>>> deactivated, all scan ranges of a >>>>> block are mapped to the one host, >>>>> where the block is located on. >>>>> This host is the "executor"/reader >>>>> for all the scan ranges of this >>>>> block. Is that correct? >>>>> >>>>> >>>>> Yes, assuming you are using HDFS. >>>>> >>>>> >>>>> I tried to visualize my >>>>> understanding of the scan_range to >>>>> host mapping for my use case (s. >>>>> attachment). Could you please have >>>>> a quick look at it and tell me if >>>>> this is correct? >>>>> >>>>> "The existing scan range >>>>> assignment is scan-node centric. >>>>> For each scan node, we >>>>> independently decide which of its >>>>> scan ranges should be processed by >>>>> which host." >>>>> Without replicas, all scan ranges >>>>> of a block would be assigend to >>>>> the same host where this block is >>>>> located on. Isn't everything local >>>>> here, so that Table_A - Block_0 >>>>> and Table_B - Block_0 can be >>>>> joined local or are further steps >>>>> neccessary? The condition in the >>>>> DistributedPlanner you pointed to >>>>> me is set to false (no exchange >>>>> nodes). >>>>> >>>>> "You want it to be host-centric. >>>>> For each host, collect the local >>>>> scan ranges of *all* scan nodes, >>>>> and assign them to that host." >>>>> Wouldn't the standard setup from >>>>> above work? Wouldn't I assign all >>>>> (the same) scan ranges to each >>>>> host in this case here? >>>>> >>>>> >>>>> The standard setup works only in if >>>>> every block only has exactly one >>>>> replica. For our purposes, that is >>>>> basically never the case (who would >>>>> store production data without >>>>> replication?), so the single-replica >>>>> assumption was not clear to me. >>>>> >>>>> Does your current setup (only changing >>>>> the planner and not the scheduler) >>>>> produce the expected results? >>>>> >>>>> >>>>> >>>>> Thank you very much! >>>>> >>>>> Best regards >>>>> Philipp >>>>> >>>>> 2018-03-28 21:04 GMT+02:00 Philipp >>>>> Krause >>>>> >>>> >: >>>>> >>>>> Thank you for your answer and >>>>> sorry for my delay! >>>>> >>>>> If my understanding is >>>>> correct, the list of scan >>>>> nodes consists of all nodes >>>>> which contain a *local* block >>>>> from a table that is needed >>>>> for the query (Assumption: I >>>>> have no replicas in my first >>>>> tests). If TableA-Block0 is on >>>>> Node_0, isn't Node_0 >>>>> automatically a scan node? And >>>>> wouldn't this scan node always >>>>> be the host for the complete >>>>> scan range(s) then? >>>>> >>>>> "For each scan node, we >>>>> independently decide which of >>>>> its scan ranges should be >>>>> processed by which host." >>>>> >>>>> https://github.com/cloudera/Impala/blob/cdh5-trunk/be/src/scheduling/scheduler.cc#L532 >>>>> >>>>> >>>>> // Loop over all scan ranges, >>>>> select an executor for those >>>>> with local impalads and >>>>> // collect all others for >>>>> later processing. >>>>> >>>>> So in this whole block, scan >>>>> ranges are assigned to the >>>>> closest executor (=host?). But >>>>> isn't the closest executor >>>>> always the node the block is >>>>> located on (assumed impalad is >>>>> installed and I have no >>>>> replicas)? And isn't this node >>>>> always a scan node at the same >>>>> time? Otherwise a thread on a >>>>> remote host had to read the >>>>> corresponding scan range, >>>>> which would be more expensive. >>>>> The only exception I can think >>>>> of is when all threads on the >>>>> local node are busy. Or, if I >>>>> use replicas and all other >>>>> threads of my node with the >>>>> "original" block are busy, a >>>>> thread on another node which >>>>> contains a replica could read >>>>> a special scan range of its >>>>> local block. Is my >>>>> understanding correct here? >>>>> >>>>> Aren't all scan ranges read >>>>> locally by its scan nodes if I >>>>> have impalad installed on all >>>>> nodes? And am I right, that >>>>> the scan range is only based >>>>> on its length which refers to >>>>> maxScanRangeLength in >>>>> computeScanRangeLocations? >>>>> https://github.com/cloudera/Impala/blob/cdh5-trunk/fe/src/main/java/org/apache/impala/planner/HdfsScanNode.java#L721 >>>>> >>>>> >>>>> I hope you can help me with >>>>> the scan node <-> scan >>>>> range->host relationship. If I >>>>> have Table_A-Block_0 and >>>>> Table_B_Block_0 on the same >>>>> node (which I want to join >>>>> locally), I don't get the >>>>> point of why scan ranges could >>>>> be assigned to another host in >>>>> my scenario. >>>>> >>>>> Best regads and thank you very >>>>> much! >>>>> Philipp Krause >>>>> >>>>> >>>>> Am 21.03.2018 um 05:21 schrieb >>>>> Alexander Behm: >>>>>> Thanks for following up. I >>>>>> think I understand your setup. >>>>>> >>>>>> If you want to not think >>>>>> about scan ranges, then you >>>>>> can modify >>>>>> HdfsScanNode.computeScanRangeLocations(). >>>>>> For example, you could change >>>>>> it to produce one scan range >>>>>> per file or per HDFS block. >>>>>> That way you'd know exactly >>>>>> what a scan range corresponds to. >>>>>> >>>>>> I think the easiest/fastest >>>>>> way for you to make progress >>>>>> is to re-implement the >>>>>> existing scan range >>>>>> assignment logic in that >>>>>> place in the code I had >>>>>> pointed you to. There is no >>>>>> quick fix to change the >>>>>> existing behavior. >>>>>> The existing scan range >>>>>> assignment is scan-node >>>>>> centric. For each scan node, >>>>>> we independently decide which >>>>>> of its scan ranges should be >>>>>> processed by which host. >>>>>> >>>>>> I believe an algorithm to >>>>>> achieve your goal would look >>>>>> completely different. You >>>>>> want it to be host-centric. >>>>>> For each host, collect the >>>>>> local scan ranges of *all* >>>>>> scan nodes, and assign them >>>>>> to that host. >>>>>> >>>>>> Does that make sense? >>>>>> >>>>>> Alex >>>>>> >>>>>> >>>>>> On Mon, Mar 19, 2018 at 1:02 >>>>>> PM, Philipp Krause >>>>>> >>>>> > >>>>>> wrote: >>>>>> >>>>>> I'd like to provide a >>>>>> small example for our >>>>>> purpose. The last post >>>>>> may be a bit confusing, >>>>>> so here's a very simple >>>>>> example in the attached >>>>>> pdf file. I hope, it's >>>>>> understandable. >>>>>> Otherwise, please give me >>>>>> a short feedback. >>>>>> >>>>>> Basically, I only want >>>>>> each data node to join >>>>>> all it's local blocks. Is >>>>>> there a range mapping >>>>>> needed or is it possible >>>>>> to easily join all local >>>>>> blocks (regardless of its >>>>>> content) since everything >>>>>> is already "prepared"? >>>>>> Maybe you can clarify >>>>>> this for me. >>>>>> >>>>>> As you can see in the >>>>>> example, the tables are >>>>>> not partitioned by ID. >>>>>> The files are manually >>>>>> prepared by the help of >>>>>> the modulo function. So I >>>>>> don't have a range like >>>>>> [0,10], but something >>>>>> like 0,5,10,15 etc. >>>>>> >>>>>> I hope, I didn't make it >>>>>> too complicated and >>>>>> confusing. I think, the >>>>>> actual idea behind this >>>>>> is really simple and I >>>>>> hope you can help me to >>>>>> get this working. >>>>>> >>>>>> Best regards and thank >>>>>> you very much for your time! >>>>>> Philipp >>>>>> >>>>>> >>>>>> Am 18.03.2018 um 17:32 >>>>>> schrieb Philipp Krause: >>>>>>> >>>>>>> Hi! At the moment the >>>>>>> data to parquet (block) >>>>>>> mapping is based on a >>>>>>> simple modulo function: >>>>>>> Id % #data_nodes. So >>>>>>> with 5 data nodes all >>>>>>> rows with Id's >>>>>>> 0,5,10,... are written >>>>>>> to Parquet_0, Id's 1,4,9 >>>>>>> are written to Parquet_1 >>>>>>> etc. That's what I did >>>>>>> manually. Since the >>>>>>> parquet file size and >>>>>>> the block size are both >>>>>>> set to 64MB, each >>>>>>> parquet file will result >>>>>>> in one block when I >>>>>>> transfer the parquet >>>>>>> files to HDFS. By >>>>>>> default, HDFS >>>>>>> distributes the blocks >>>>>>> randomly. For test >>>>>>> purposes I transferred >>>>>>> corresponding blocks >>>>>>> from Table_A and Table_B >>>>>>> to the same data node >>>>>>> (Table_A - Block_X with >>>>>>> Id's 0,5,10 and Table_B >>>>>>> - Block_Y with Id's >>>>>>> 0,5,10). In this case, >>>>>>> they are transferred to >>>>>>> data_node_0 because the >>>>>>> modulo function (which I >>>>>>> want to implement in the >>>>>>> scheduler) returns 0 for >>>>>>> these Id's. This is also >>>>>>> done manually at the moment. >>>>>>> >>>>>>> 1.) DistributedPlanner: >>>>>>> For first, upcoming >>>>>>> tests I simply changed >>>>>>> the first condition in >>>>>>> the DistributedPlanner >>>>>>> to true to avoid >>>>>>> exchange nodes. >>>>>>> >>>>>>> 2.) The scheduler: >>>>>>> That's the part I'm >>>>>>> currently struggling >>>>>>> with. For first tests, >>>>>>> block replication is >>>>>>> deactivated. I'm not >>>>>>> sure how / where to >>>>>>> implement the modulo >>>>>>> function for scan range >>>>>>> to host mapping. Without >>>>>>> the modulo function, I >>>>>>> had to implement a hard >>>>>>> coded mapping (something >>>>>>> like "range" 0-0, 5-5, >>>>>>> 10-10 -> Data_node_0 >>>>>>> etc.). Is that correct? >>>>>>> Instead I would like to >>>>>>> use a slightly more >>>>>>> flexible solution by the >>>>>>> help of this modulo >>>>>>> function for the host >>>>>>> mapping. >>>>>>> >>>>>>> I would be really >>>>>>> grateful if you could >>>>>>> give me a hint for the >>>>>>> scheduling >>>>>>> implementation. I try to >>>>>>> go deeper through the >>>>>>> code meanwhile. >>>>>>> >>>>>>> Best regards and thank >>>>>>> you in advance >>>>>>> Philipp >>>>>>> >>>>>>> >>>>>>> Am 14.03.2018 um 08:06 >>>>>>> schrieb Philipp Krause: >>>>>>>> Thank you very much for >>>>>>>> these information! I'll >>>>>>>> try to implement these >>>>>>>> two steps and post some >>>>>>>> updates within the next >>>>>>>> days! >>>>>>>> >>>>>>>> Best regards >>>>>>>> Philipp >>>>>>>> >>>>>>>> 2018-03-13 5:38 >>>>>>>> GMT+01:00 Alexander >>>>>>>> Behm >>>>>>>> >>>>>>> >: >>>>>>>> >>>>>>>> Cool that you >>>>>>>> working on a >>>>>>>> research project >>>>>>>> with Impala! >>>>>>>> >>>>>>>> Properly adding >>>>>>>> such a feature to >>>>>>>> Impala is a >>>>>>>> substantial effort, >>>>>>>> but hacking the >>>>>>>> code for an >>>>>>>> experiment or two >>>>>>>> seems doable. >>>>>>>> >>>>>>>> I think you will >>>>>>>> need to modify two >>>>>>>> things: (1) the >>>>>>>> planner to not add >>>>>>>> exchange nodes, and >>>>>>>> (2) the scheduler >>>>>>>> to assign the >>>>>>>> co-located scan >>>>>>>> ranges to the same >>>>>>>> host. >>>>>>>> >>>>>>>> Here are a few >>>>>>>> starting points in >>>>>>>> the code: >>>>>>>> >>>>>>>> 1) DistributedPlanner >>>>>>>> https://github.com/apache/impala/blob/master/fe/src/main/java/org/apache/impala/planner/DistributedPlanner.java#L318 >>>>>>>> >>>>>>>> >>>>>>>> The first condition >>>>>>>> handles the case >>>>>>>> where no exchange >>>>>>>> nodes need to be >>>>>>>> added because the >>>>>>>> join inputs are >>>>>>>> already suitably >>>>>>>> partitioned. >>>>>>>> You could hack the >>>>>>>> code to always go >>>>>>>> into that codepath, >>>>>>>> so no exchanges are >>>>>>>> added. >>>>>>>> >>>>>>>> 2) The scheduler >>>>>>>> https://github.com/apache/impala/blob/master/be/src/scheduling/scheduler.cc#L226 >>>>>>>> >>>>>>>> >>>>>>>> You'll need to dig >>>>>>>> through and >>>>>>>> understand that >>>>>>>> code so that you >>>>>>>> can make the >>>>>>>> necessary changes. >>>>>>>> Change the scan >>>>>>>> range to host >>>>>>>> mapping to your >>>>>>>> liking. The rest of >>>>>>>> the code should >>>>>>>> just work. >>>>>>>> >>>>>>>> Cheers, >>>>>>>> >>>>>>>> Alex >>>>>>>> >>>>>>>> >>>>>>>> On Mon, Mar 12, >>>>>>>> 2018 at 6:55 PM, >>>>>>>> Philipp Krause >>>>>>>> >>>>>>> > >>>>>>>> wrote: >>>>>>>> >>>>>>>> Thank you very >>>>>>>> much for your >>>>>>>> quick answers! >>>>>>>> The intention >>>>>>>> behind this is >>>>>>>> to improve the >>>>>>>> execution time >>>>>>>> and (primarily) >>>>>>>> to examine the >>>>>>>> impact of >>>>>>>> block-co-location >>>>>>>> (research >>>>>>>> project) for >>>>>>>> this particular >>>>>>>> query >>>>>>>> (simplified): >>>>>>>> >>>>>>>> select A.x, >>>>>>>> B.y, A.z from >>>>>>>> tableA as A >>>>>>>> inner join >>>>>>>> tableB as B on >>>>>>>> A.id=B.id >>>>>>>> >>>>>>>> The "real" >>>>>>>> query includes >>>>>>>> three joins and >>>>>>>> the data size >>>>>>>> is in pb-range. >>>>>>>> Therefore >>>>>>>> several nodes >>>>>>>> (5 in the test >>>>>>>> environment >>>>>>>> with less data) >>>>>>>> are used >>>>>>>> (without any >>>>>>>> load balancer). >>>>>>>> >>>>>>>> Could you give >>>>>>>> me some hints >>>>>>>> what code >>>>>>>> changes are >>>>>>>> required and >>>>>>>> which files are >>>>>>>> affected? I >>>>>>>> don't know how >>>>>>>> to give Impala >>>>>>>> the information >>>>>>>> that it should >>>>>>>> only join the >>>>>>>> local data >>>>>>>> blocks on each >>>>>>>> node and then >>>>>>>> pass it to the >>>>>>>> "final" node >>>>>>>> which receives >>>>>>>> all >>>>>>>> intermediate >>>>>>>> results. I hope >>>>>>>> you can help me >>>>>>>> to get this >>>>>>>> working. That >>>>>>>> would be awesome! >>>>>>>> >>>>>>>> Best regards >>>>>>>> Philipp >>>>>>>> >>>>>>>> Am 12.03.2018 >>>>>>>> um 18:38 >>>>>>>> schrieb >>>>>>>> Alexander Behm: >>>>>>>>> I suppose one >>>>>>>>> exception is >>>>>>>>> if your data >>>>>>>>> lives only on >>>>>>>>> a single node. >>>>>>>>> Then you can >>>>>>>>> set >>>>>>>>> num_nodes=1 >>>>>>>>> and make sure >>>>>>>>> to send the >>>>>>>>> query request >>>>>>>>> to the impalad >>>>>>>>> running on the >>>>>>>>> same data node >>>>>>>>> as the target >>>>>>>>> data. Then you >>>>>>>>> should get a >>>>>>>>> local join. >>>>>>>>> >>>>>>>>> On Mon, Mar >>>>>>>>> 12, 2018 at >>>>>>>>> 9:30 AM, >>>>>>>>> Alexander Behm >>>>>>>>> >>>>>>>> > >>>>>>>>> wrote: >>>>>>>>> >>>>>>>>> Such a >>>>>>>>> specific >>>>>>>>> block >>>>>>>>> arrangement >>>>>>>>> is very >>>>>>>>> uncommon >>>>>>>>> for >>>>>>>>> typical >>>>>>>>> Impala >>>>>>>>> setups, so >>>>>>>>> we don't >>>>>>>>> attempt to >>>>>>>>> recognize >>>>>>>>> and >>>>>>>>> optimize >>>>>>>>> this >>>>>>>>> narrow >>>>>>>>> case. In >>>>>>>>> particular, >>>>>>>>> such an >>>>>>>>> arrangement >>>>>>>>> tends to >>>>>>>>> be short >>>>>>>>> lived if >>>>>>>>> you have >>>>>>>>> the HDFS >>>>>>>>> balancer >>>>>>>>> turned on. >>>>>>>>> >>>>>>>>> Without >>>>>>>>> making >>>>>>>>> code >>>>>>>>> changes, >>>>>>>>> there is >>>>>>>>> no way >>>>>>>>> today to >>>>>>>>> remove the >>>>>>>>> data >>>>>>>>> exchanges >>>>>>>>> and make >>>>>>>>> sure that >>>>>>>>> the >>>>>>>>> scheduler >>>>>>>>> assigns >>>>>>>>> scan >>>>>>>>> splits to >>>>>>>>> nodes in >>>>>>>>> the >>>>>>>>> desired >>>>>>>>> way >>>>>>>>> (co-located, >>>>>>>>> but with >>>>>>>>> possible >>>>>>>>> load >>>>>>>>> imbalance). >>>>>>>>> >>>>>>>>> In what >>>>>>>>> way is the >>>>>>>>> current >>>>>>>>> setup >>>>>>>>> unacceptable >>>>>>>>> to you? Is >>>>>>>>> this >>>>>>>>> pre-mature >>>>>>>>> optimization? >>>>>>>>> If you >>>>>>>>> have >>>>>>>>> certain >>>>>>>>> performance >>>>>>>>> expectations/requirements >>>>>>>>> for >>>>>>>>> specific >>>>>>>>> queries we >>>>>>>>> might be >>>>>>>>> able to >>>>>>>>> help you >>>>>>>>> improve >>>>>>>>> those. If >>>>>>>>> you want >>>>>>>>> to pursue >>>>>>>>> this >>>>>>>>> route, >>>>>>>>> please >>>>>>>>> help us by >>>>>>>>> posting >>>>>>>>> complete >>>>>>>>> query >>>>>>>>> profiles. >>>>>>>>> >>>>>>>>> Alex >>>>>>>>> >>>>>>>>> On Mon, >>>>>>>>> Mar 12, >>>>>>>>> 2018 at >>>>>>>>> 6:29 AM, >>>>>>>>> Philipp >>>>>>>>> Krause >>>>>>>>> >>>>>>>> > >>>>>>>>> wrote: >>>>>>>>> >>>>>>>>> Hello >>>>>>>>> everyone! >>>>>>>>> >>>>>>>>> In >>>>>>>>> order >>>>>>>>> to >>>>>>>>> prevent >>>>>>>>> network >>>>>>>>> traffic, >>>>>>>>> I'd >>>>>>>>> like >>>>>>>>> to >>>>>>>>> perform >>>>>>>>> local >>>>>>>>> joins >>>>>>>>> on >>>>>>>>> each >>>>>>>>> node >>>>>>>>> instead >>>>>>>>> of >>>>>>>>> exchanging >>>>>>>>> the >>>>>>>>> data >>>>>>>>> and >>>>>>>>> perform >>>>>>>>> a join >>>>>>>>> over >>>>>>>>> the >>>>>>>>> complete >>>>>>>>> data >>>>>>>>> afterwards. >>>>>>>>> My >>>>>>>>> query >>>>>>>>> is >>>>>>>>> basically >>>>>>>>> a join >>>>>>>>> over >>>>>>>>> three >>>>>>>>> three >>>>>>>>> tables >>>>>>>>> on an >>>>>>>>> ID >>>>>>>>> attribute. >>>>>>>>> The >>>>>>>>> blocks >>>>>>>>> are >>>>>>>>> perfectly >>>>>>>>> distributed, >>>>>>>>> so >>>>>>>>> that >>>>>>>>> e.g. >>>>>>>>> Table >>>>>>>>> A - >>>>>>>>> Block >>>>>>>>> 0  and >>>>>>>>> Table >>>>>>>>> B - >>>>>>>>> Block >>>>>>>>> 0  are >>>>>>>>> on the >>>>>>>>> same >>>>>>>>> node. >>>>>>>>> These >>>>>>>>> blocks >>>>>>>>> contain >>>>>>>>> all >>>>>>>>> data >>>>>>>>> rows >>>>>>>>> with >>>>>>>>> an ID >>>>>>>>> range >>>>>>>>> [0,1]. >>>>>>>>> Table >>>>>>>>> A - >>>>>>>>> Block >>>>>>>>> 1  and >>>>>>>>> Table >>>>>>>>> B - >>>>>>>>> Block >>>>>>>>> 1 with >>>>>>>>> an ID >>>>>>>>> range >>>>>>>>> [2,3] >>>>>>>>> are on >>>>>>>>> another >>>>>>>>> node >>>>>>>>> etc. >>>>>>>>> So I >>>>>>>>> want >>>>>>>>> to >>>>>>>>> perform >>>>>>>>> a >>>>>>>>> local >>>>>>>>> join >>>>>>>>> per >>>>>>>>> node >>>>>>>>> because >>>>>>>>> any >>>>>>>>> data >>>>>>>>> exchange >>>>>>>>> would >>>>>>>>> be >>>>>>>>> unneccessary >>>>>>>>> (except >>>>>>>>> for >>>>>>>>> the >>>>>>>>> last >>>>>>>>> step >>>>>>>>> when >>>>>>>>> the >>>>>>>>> final >>>>>>>>> node >>>>>>>>> recevieves >>>>>>>>> all >>>>>>>>> results >>>>>>>>> of the >>>>>>>>> other >>>>>>>>> nodes). >>>>>>>>> Is >>>>>>>>> this >>>>>>>>> possible? >>>>>>>>> At the >>>>>>>>> moment >>>>>>>>> the >>>>>>>>> query >>>>>>>>> plan >>>>>>>>> includes >>>>>>>>> multiple >>>>>>>>> data >>>>>>>>> exchanges, >>>>>>>>> although >>>>>>>>> the >>>>>>>>> blocks >>>>>>>>> are >>>>>>>>> already >>>>>>>>> perfectly >>>>>>>>> distributed >>>>>>>>> (manually). >>>>>>>>> I >>>>>>>>> would >>>>>>>>> be >>>>>>>>> grateful >>>>>>>>> for >>>>>>>>> any help! >>>>>>>>> >>>>>>>>> Best >>>>>>>>> regards >>>>>>>>> Philipp >>>>>>>>> Krause >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>> >>>>>> >>>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>> >>>> >>> >>> >> >> > --------------6127C242AA748F22A9016C4D Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: 8bit

Hello Alex,

I have tried out several configurations but I still couldn't find a solution for my problem :( In the query summary (s. attachment) it looks like as if no rows are read. Do you have an idea what I have to change? I am sorry for the circumstances and thank you once more for the great support to get this working!


Am 29.04.2018 um 21:21 schrieb Philipp Krause:

Hi Alex,
I got the modified version working on my cluster. The query plan looks exactly as wanted (s. attachment). This is awesome! Unfortunately the result set is empty. As you can see in query_state.png, the scan progress always shows 50% although the query has finished.

The only modification in the code is the if statement you pointed to me (I set it to true). Maybe I have to give Impala the information about the lhs / rhs join partition since there are no exchange nodes now (like in the following lines)? The corresponding  partitions / blocks of each table are on the same node.

I think we are very close to the final result and I hope you can help me once more. Thank you so much!

Best regards
Philipp


Am 24.04.2018 um 18:00 schrieb Alexander Behm:
On Tue, Apr 24, 2018 at 5:31 AM, Philipp Krause <philippkrause.mail@googlemail.com> wrote:

To prevent the broadcast join I could simply use the shuffle operator in the query:

SELECT * FROM business_partition_1 INNER JOIN [SHUFFLE] business_partition_2 WHERE business_partition_1.businessid=business_partition_2.businessid


Not sure what version of Impala you are using, and whether hints override any changes you might make. I suggest you make the code work as you wish without requiring hints. 


I think the broadcast is currently only used because of my very small test tables.

This gives me the plan attached as partitioned_shuffle.png. Since my modified version isn't working yet, I partitioned both tables on businessid in Impala. The "hack" should only help to get into the if-condition if I partition the data manually, right?. But in this case (if the partitioning is done by Impala itself) Impala should get into the if-condition anyway. Unfortunately I can't see a difference in the plan compared to my unpartitioned tables (unpartitioned.png) concerning the exchange nodes. My goal is actually to get rid of all exchange nodes since the corresponding data is already present on each node. Actually the plan should look the same except for the 03 and 04 exchange then.


I understand the picture and goal. I suggest you read, understand, and modify the code in DistributedPlanner.createHashJoinFragment() to create the plan shape that you want.
I don't know how you are producing these plans. Are you sure your code changes are taking effect?


Are there other changes neccessary to just take Table1-partition/block X and Table2-partition/block Y on each node and join them without any data exchange? Actually each node should take all its local blocks for both tables, join them and pass the results back to the coordinator where all results come together (please see explanation.png).


No. You just need to create the plan without exchange nodes, as we've already gone through early in this thread.


I'm looking forward hearing from you. I hope we can realise this.

Best regards
Philipp


Am 24.04.2018 um 07:03 schrieb Alexander Behm:
On Mon, Apr 23, 2018 at 7:24 PM, Philipp Krause <philippkrause.mail@googlemail.com> wrote:

Hi Alex,
thanks for the information! I've compiled the cdh5.13.1-release and replaced impala-frontend-0.1-SNAPSHOT.jar (which seems to include the changes in the DistributedPlanner.java). There still seems to to be a method missing after replacing the jar but I'll try to figure that out.

I have two questions concerning the code fragment in the DistributedPlanner.java you pointed to me.

First:
The attached graphic shows the query plan for two tables which are partitioned on the join attribute (standard impala version without any changes). If I change the if-condition to true for my modified version I expect to get the same result for my "hand made" partitions (outside of impala). But why is there an exchange broadcast (even in the standard version)? I mean, if I have a partition of Table 1 with ID=0 on Node X why is there a broadcast of the partition of Table 2 with ID=0 on Node Y? Actually this partition only has to be sent to Node X where the matching partition of Table 1 is located (instead of sending it to all nodes (broadcast)). Or is this exactly the case and it's only shown as a broadcast here in the graphics?


You are reading the plan correctly. Impala simply does not implement that optimization.


Second:
All corresponding blocks of the partitioned tables are on the same node (e.g. Table 1 Partition with ID=0, Table 2 Partition with ID=0 => Node 1 etc.). This is what I did manually. As already mentioned before I want to join these partitions (blocks) locally on each node. But if I'm correct, the modification in the DistributedPlanner will also only lead to the plan in the attached graphic so that no further exchanges are created if I use my "hand made" partitions. But there is still the broadcast exchange which distributes the partitions across the cluster which isn't neccessary because all needed blocks are already on the same node and are ready to get joined locally. Is there a way to realise that and get rid of the broadcast exchange?

You are right. That hack in DistributedPlanner only works for partitioned hash joins. You can probably stitch the plan together at an earlier place, e.g.:
 

Please correct me if I'm wrong with my assumptions.

Thank you very much!

Best regards
Philipp







Am 18.04.2018 um 07:16 schrieb Alexander Behm:
Your CM-managed cluster must be running a "compatible" Impala version already for this trick to work. It looks like your catalog binary is trying to find a method which does not exist in the .jar, presumably because your .jar is built based on a different version of Impala where that method does not exist anymore.

It looks like you have CDH 5.13.3 installed. CDH 5.13.3 is based on Impala 2.10, see:

That means this binary copying trick will only work with a modified version of Impala 2.10, and very likely will not work with a different version.

It's probably easier to test with the mini cluster first. Alternatively, it "might" work if you replace all the binaries mentioned above, but it's quite possible that will not work.

On Sun, Apr 15, 2018 at 7:13 PM, Philipp Krause <philippkrause.mail@googlemail.com> wrote:

Hi Alex! Thank you for the list! The build of the modified cdh5-trunk branch (debug mode) was sucessfull. After replacing "impala-frontend-0.1-SNAPSHOT.jar" in /opt/cloudera/parcels/CDH-5.13.1-1.cdh5.13.1.p0.2/jars/ I got the following error in my existing cluster:
F0416 01:16:45.402997 17897 catalog.cc:69] NoSuchMethodError: getCatalogObjects
When I switch back to the original jar file the error is gone. So it must be something wrong with this file I guess. But I wonder about the error in catalog.cc because I didn't touch any .cc files.

I also replaced "impala-data-source-api-1.0-SNAPSHOT.jar". The other jar files do not exist in my impala installation (CDH-5.13.1).

What am I doing wrong?

Best regards
Philipp


Am 13.04.2018 um 20:12 schrieb Alexander Behm:
Here's the foll list. It might not be minimal, but copying/overwriting these should work.

debug/service/impalad
debug/service/libfesupport.so
debug/service/libService.a
release/service/impalad
release/service/libfesupport.so
release/service/libService.a
yarn-extras-0.1-SNAPSHOT.jar
impala-data-source-api-1.0-SNAPSHOT-sources.jar
impala-data-source-api-1.0-SNAPSHOT.jar
impala-frontend-0.1-SNAPSHOT-tests.jar
impala-frontend-0.1-SNAPSHOT.jar
libkudu_client.so.0.1.0
libstdc++.so.6.0.20
impala-no-sse.bc
impala-sse.bc
libimpalalzo.so

If you are only modifying the Java portion (like DistributedPlanner), then only copying/replacing the *.jar files should be sufficient.

On Fri, Apr 13, 2018 at 11:00 AM, Philipp Krause <philippkrause.mail@googlemail.com> wrote:
Yes, I have a running (virtual) cluster. I would try to follow your way with the custom impala build (DistributedPlanner.java is the only modified file at the moment). Thank you in advance for the file list! 

Best regards 
Philipp

Alexander Behm <alex.behm@cloudera.com> schrieb am Fr., 13. Apr. 2018, 18:45:
I'm not really following your installation/setup and am not an expert on Cloudera Manager installation/config. If you are going to build Impala anyway, it's probably easiest to test on Impala's minicluster first.

In general, if you have a running Cloudera Managed cluster, you can deploy a custom Impala build by simply overwriting the Impala existing binaries and jars with the new build. If you want to go this route, I can give you a full list of files you need to replace.

On Tue, Apr 10, 2018 at 11:44 AM, Philipp Krause <philippkrause.mail@googlemail.com> wrote:
Thank you for the explanation! Yes, I'm using HDFS. The single replica setup is only for test purposes at the moment. I think this makes it easier to gain some first results since less modifications (scheduler etc.) are neccessary.
I would like to test the DistributedPlanner modification in my virtual cluster. I used a customized Vagrant script to install Impala on multiple hosts (s.attachment). It simply installs cloudera-manager-server-db, cloudera-manager-server and cloudera-manager-daemons via apt-get. What would be the simplest solution to setup my modified version? Could I simply call ./buildall.sh and change the script to sth. like this?

echo "Install java..."
apt-get -q -y --force-yes install oracle-j2sdk1.7
echo "Download impala..."
wget https://... where I uploaded my modified version
echo "Extract impala..."
tar -xvzf Impala-cdh5-trunk.tar.gz
cd Impala-cdh5-trunk
echo "Build impala..."
./buildall.sh
echo "Start impala instances..."
service cloudera-scm-server-db initdb
service cloudera-scm-server-db start
service cloudera-scm-server start

Or is there another, maybe even easier method, to test the code? Maybe via bootstrap_development.sh / minicluster? 

Best regards
Philipp

2018-04-05 18:39 GMT+02:00 Alexander Behm <alex.behm@cloudera.com>:
Apologies for the late response. Btw, your previous post was clear enough to me, so no worries :)


On Wed, Apr 4, 2018 at 7:46 AM, Philipp Krause <philippkrause.mail@googlemail.com> wrote:
Hello Alex,

I think my previous post has been too long and confusing. I apologize for that!

If replicas are completely deactivated, all scan ranges of a block are mapped to the one host, where the block is located on. This host is the "executor"/reader for all the scan ranges of this block. Is that correct?

Yes, assuming you are using HDFS.
 

I tried to visualize my understanding of the scan_range to host mapping for my use case (s. attachment). Could you please have a quick look at it and tell me if this is correct?

"The existing scan range assignment is scan-node centric. For each scan node, we independently decide which of its scan ranges should be processed by which host."
Without replicas, all scan ranges of a block would be assigend to the same host where this block is located on. Isn't everything local here, so that Table_A - Block_0 and Table_B - Block_0 can be joined local or are further steps neccessary? The condition in the DistributedPlanner you pointed to me is set to false (no exchange nodes).

"You want it to be host-centric. For each host, collect the local scan ranges of *all* scan nodes, and assign them to that host."
Wouldn't the standard setup from above work? Wouldn't I assign all (the same) scan ranges to each host in this case here?

The standard setup works only in if every block only has exactly one replica. For our purposes, that is basically never the case (who would store production data without replication?), so the single-replica assumption was not clear to me.

Does your current setup (only changing the planner and not the scheduler) produce the expected results?



Thank you very much!

Best regards
Philipp

2018-03-28 21:04 GMT+02:00 Philipp Krause <philippkrause.mail@googlemail.com>:

Thank you for your answer and sorry for my delay!

If my understanding is correct, the list of scan nodes consists of all nodes which contain a *local* block from a table that is needed for the query (Assumption: I have no replicas in my first tests). If TableA-Block0 is on Node_0, isn't Node_0 automatically a scan node? And wouldn't this scan node always be the host for the complete scan range(s) then?

"For each scan node, we independently decide which of its scan ranges should be processed by which host."

https://github.com/cloudera/Impala/blob/cdh5-trunk/be/src/scheduling/scheduler.cc#L532
// Loop over all scan ranges, select an executor for those with local impalads and
// collect all others for later processing.

So in this whole block, scan ranges are assigned to the closest executor (=host?). But isn't the closest executor always the node the block is located on (assumed impalad is installed and I have no replicas)? And isn't this node always a scan node at the same time? Otherwise a thread on a remote host had to read the corresponding scan range, which would be more expensive. The only exception I can think of is when all threads on the local node are busy. Or, if I use replicas and all other threads of my node with the "original" block are busy, a thread on another node which contains a replica could read a special scan range of its local block. Is my understanding correct here?

Aren't all scan ranges read locally by its scan nodes if I have impalad installed on all nodes? And am I right, that the scan range is only based on its length which refers to maxScanRangeLength in computeScanRangeLocations?
https://github.com/cloudera/Impala/blob/cdh5-trunk/fe/src/main/java/org/apache/impala/planner/HdfsScanNode.java#L721

I hope you can help me with the scan node <-> scan range->host relationship. If I have Table_A-Block_0 and Table_B_Block_0 on the same node (which I want to join locally), I don't get the point of why scan ranges could be assigned to another host in my scenario.

Best regads and thank you very much!
Philipp Krause


Am 21.03.2018 um 05:21 schrieb Alexander Behm:
Thanks for following up. I think I understand your setup.

If you want to not think about scan ranges, then you can modify HdfsScanNode.computeScanRangeLocations(). For example, you could change it to produce one scan range per file or per HDFS block. That way you'd know exactly what a scan range corresponds to.

I think the easiest/fastest way for you to make progress is to re-implement the existing scan range assignment logic in that place in the code I had pointed you to. There is no quick fix to change the existing behavior.
The existing scan range assignment is scan-node centric. For each scan node, we independently decide which of its scan ranges should be processed by which host.

I believe an algorithm to achieve your goal would look completely different. You want it to be host-centric. For each host, collect the local scan ranges of *all* scan nodes, and assign them to that host.

Does that make sense?

Alex


On Mon, Mar 19, 2018 at 1:02 PM, Philipp Krause <philippkrause.mail@googlemail.com> wrote:

I'd like to provide a small example for our purpose. The last post may be a bit confusing, so here's a very simple example in the attached pdf file. I hope, it's understandable. Otherwise, please give me a short feedback.

Basically, I only want each data node to join all it's local blocks. Is there a range mapping needed or is it possible to easily join all local blocks (regardless of its content) since everything is already "prepared"? Maybe you can clarify this for me.

As you can see in the example, the tables are not partitioned by ID. The files are manually prepared by the help of the modulo function. So I don't have a range like [0,10], but something like 0,5,10,15 etc.

I hope, I didn't make it too complicated and confusing. I think, the actual idea behind this is really simple and I hope you can help me to get this working.

Best regards and thank you very much for your time!
Philipp


Am 18.03.2018 um 17:32 schrieb Philipp Krause:

Hi! At the moment the data to parquet (block) mapping is based on a simple modulo function: Id % #data_nodes. So with 5 data nodes all rows with Id's 0,5,10,... are written to Parquet_0, Id's 1,4,9 are written to Parquet_1 etc. That's what I did manually. Since the parquet file size and the block size are both set to 64MB, each parquet file will result in one block when I transfer the parquet files to HDFS. By default, HDFS distributes the blocks randomly. For test purposes I transferred corresponding blocks from Table_A and Table_B to the same data node (Table_A - Block_X with Id's 0,5,10 and Table_B - Block_Y with Id's 0,5,10). In this case, they are transferred to data_node_0 because the modulo function (which I want to implement in the scheduler) returns 0 for these Id's. This is also done manually at the moment.

1.) DistributedPlanner: For first, upcoming tests I simply changed the first condition in the DistributedPlanner to true to avoid exchange nodes.

2.) The scheduler: That's the part I'm currently struggling with. For first tests, block replication is deactivated. I'm not sure how / where to implement the modulo function for scan range to host mapping. Without the modulo function, I had to implement a hard coded mapping (something like "range" 0-0, 5-5, 10-10 -> Data_node_0 etc.). Is that correct? Instead I would like to use a slightly more flexible solution by the help of this modulo function for the host mapping.

I would be really grateful if you could give me a hint for the scheduling implementation. I try to go deeper through the code meanwhile.

Best regards and thank you in advance
Philipp


Am 14.03.2018 um 08:06 schrieb Philipp Krause:
Thank you very much for these information! I'll try to implement these two steps and post some updates within the next days!

Best regards
Philipp

2018-03-13 5:38 GMT+01:00 Alexander Behm <alex.behm@cloudera.com>:
Cool that you working on a research project with Impala!

Properly adding such a feature to Impala is a substantial effort, but hacking the code for an experiment or two seems doable.

I think you will need to modify two things: (1) the planner to not add exchange nodes, and (2) the scheduler to assign the co-located scan ranges to the same host.

Here are a few starting points in the code:

1) DistributedPlanner

The first condition handles the case where no exchange nodes need to be added because the join inputs are already suitably partitioned.
You could hack the code to always go into that codepath, so no exchanges are added.

2) The scheduler

You'll need to dig through and understand that code so that you can make the necessary changes. Change the scan range to host mapping to your liking. The rest of the code should just work.

Cheers,

Alex


On Mon, Mar 12, 2018 at 6:55 PM, Philipp Krause <philippkrause.mail@googlemail.com> wrote:

Thank you very much for your quick answers!
The intention behind this is to improve the execution time and (primarily) to examine the impact of block-co-location (research project) for this particular query (simplified):

select A.x, B.y, A.z from tableA as A inner join tableB as B on A.id=B.id

The "real" query includes three joins and the data size is in pb-range. Therefore several nodes (5 in the test environment with less data) are used (without any load balancer).

Could you give me some hints what code changes are required and which files are affected? I don't know how to give Impala the information that it should only join the local data blocks on each node and then pass it to the "final" node which receives all intermediate results. I hope you can help me to get this working. That would be awesome!

Best regards
Philipp

Am 12.03.2018 um 18:38 schrieb Alexander Behm:
I suppose one exception is if your data lives only on a single node. Then you can set num_nodes=1 and make sure to send the query request to the impalad running on the same data node as the target data. Then you should get a local join.

On Mon, Mar 12, 2018 at 9:30 AM, Alexander Behm <alex.behm@cloudera.com> wrote:
Such a specific block arrangement is very uncommon for typical Impala setups, so we don't attempt to recognize and optimize this narrow case. In particular, such an arrangement tends to be short lived if you have the HDFS balancer turned on.

Without making code changes, there is no way today to remove the data exchanges and make sure that the scheduler assigns scan splits to nodes in the desired way (co-located, but with possible load imbalance).

In what way is the current setup unacceptable to you? Is this pre-mature optimization? If you have certain performance expectations/requirements for specific queries we might be able to help you improve those. If you want to pursue this route, please help us by posting complete query profiles.

Alex

On Mon, Mar 12, 2018 at 6:29 AM, Philipp Krause <philippkrause.mail@googlemail.com> wrote:
Hello everyone!

In order to prevent network traffic, I'd like to perform local joins on each node instead of exchanging the data and perform a join over the complete data afterwards. My query is basically a join over three three tables on an ID attribute. The blocks are perfectly distributed, so that e.g. Table A - Block 0  and Table B - Block 0  are on the same node. These blocks contain all data rows with an ID range [0,1]. Table A - Block 1  and Table B - Block 1 with an ID range [2,3] are on another node etc. So I want to perform a local join per node because any data exchange would be unneccessary (except for the last step when the final node recevieves all results of the other nodes). Is this possible?
At the moment the query plan includes multiple data exchanges, although the blocks are already perfectly distributed (manually).
I would be grateful for any help!

Best regards
Philipp Krause























--------------6127C242AA748F22A9016C4D-- --------------CB415E39653E950D398ABE97 Content-Type: image/png; name="query_summary.png" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="query_summary.png" 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 +/8BKxCXIwf6x/EAAAAASUVORK5CYII= --------------CB415E39653E950D398ABE97--