{"id":414937,"date":"2024-06-30T00:03:44","date_gmt":"2024-06-30T00:03:44","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=414937"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-30T00:03:44","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=414937","title":{"rendered":"<span>Big \/ Bug Data: Analyzing the Apache Flink Source Code<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/0df\/283\/b23\/0df283b236b743464ffc2f254f0a6ab6.png\" alt=\"image1.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/0df\/283\/b23\/0df283b236b743464ffc2f254f0a6ab6.png\"\/><\/div>\n<p>  Applications used in the field of Big Data process huge amounts of information, and this often happens in real time. Naturally, such applications must be highly reliable so that no error in the code can interfere with data processing. To achieve high reliability, one needs to keep a wary eye on the code quality of projects developed for this area. The PVS-Studio static analyzer is one of the solutions to this problem. Today, the Apache Flink project developed by the Apache Software Foundation, one of the leaders in the Big Data software market, was chosen as a test subject for the analyzer.<br \/>  <a name=\"habracut\"><\/a><br \/>  So, what is Apache Flink? It is an open-source framework for distributed processing of large amounts of data. It was developed as an alternative to Hadoop MapReduce in 2010 at the Technical University of Berlin. The framework is based on the distributed execution engine for batch and streaming data processing applications. This engine is written in Java and Scala. Today, Apache Flink can be used in projects written using Java, Scala, Python, and even SQL.<\/p>\n<h2>Project analysis<\/h2>\n<p>  After downloading the project&#8217;s source code, I started building it using the &#8216;mvn clean package -DskipTests&#8217; command specified in the instructions on <a href=\"https:\/\/github.com\/apache\/flink\">GitHub<\/a>. While the build was in progress, I used the <a href=\"https:\/\/github.com\/AlDanial\/cloc\">CLOC<\/a> utility and found out that there were 10838 Java files in the project, which had about 1.3 million lines of code. Moreover, there were as many as 3833 test Java files, which is more than 1\/3 of all Java files. I also noticed that the project uses the FindBugs static code analyzer and the Cobertura utility. The latter provides information about code coverage by tests. Given all this, it becomes clear that the developers of Apache Flink carefully monitored the quality of the code and test coverage during development.<\/p>\n<p>  After a successful build, I opened the project in IntelliJ IDEA and started the analysis using the <a href=\"https:\/\/plugins.jetbrains.com\/plugin\/12263-pvs-studio-for-idea-and-android-studio\">PVS-Studio for IDEA and Android Studio<\/a> plugin. The analyzer&#8217;s warnings distributed as follows:<\/p>\n<ul>\n<li>183 High;<\/li>\n<li>759 Medium;<\/li>\n<li>545 Low.<\/li>\n<\/ul>\n<p>  Approximately 2\/3 of the PVS-Studio analyzer&#8217;s warnings were issued for test files. If we take into account this fact and the size of the project&#8217;s code base, we can say for sure that the developers of Apache Flink managed to keep the code quality at a high level.<\/p>\n<p>  After studying the analyzer&#8217;s warnings in more detail, I chose the most interesting ones in my opinion. So let&#8217;s see what PVS-Studio found in this project!<\/p>\n<p>  <a href=\"https:\/\/viva64.com\/en\/pvs-studio-download\/?utm_source=habr&amp;utm_medium=banner&amp;utm_campaign=0781_Apache_Flink\"><\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/42b\/757\/14b\/42b75714b45fa154c5497bdb32b2c568.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/42b\/757\/14b\/42b75714b45fa154c5497bdb32b2c568.png\"\/><\/div>\n<p><\/a>  <\/p>\n<h2>Just a little carelessness<\/h2>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v6001\/\">V6001<\/a> There are identical sub-expressions &#8216;processedData&#8217; to the left and to the right of the &#8216;==&#8217; operator. CheckpointStatistics.java(229)<\/p>\n<pre><code class=\"java\">@Override public boolean equals(Object o)  {   ....   CheckpointStatistics that = (CheckpointStatistics) o;   return id == that.id &amp;&amp;     savepoint == that.savepoint &amp;&amp;     triggerTimestamp == that.triggerTimestamp &amp;&amp;     latestAckTimestamp == that.latestAckTimestamp &amp;&amp;     stateSize == that.stateSize &amp;&amp;     duration == that.duration &amp;&amp;     alignmentBuffered == that.alignmentBuffered &amp;&amp;     processedData == processedData &amp;&amp;                \/\/ &lt;=     persistedData == that.persistedData &amp;&amp;     numSubtasks == that.numSubtasks &amp;&amp;     numAckSubtasks == that.numAckSubtasks &amp;&amp;     status == that.status &amp;&amp;     Objects.equals(checkpointType, that.checkpointType) &amp;&amp;     Objects.equals(       checkpointStatisticsPerTask,        that.checkpointStatisticsPerTask); }<\/code><\/pre>\n<p>  This error is not very noticeable among other expressions in <i>return<\/i>. When overriding the <i>equals<\/i> method for the <i>CheckpointStatistics<\/i> class, the programmer made an error in the <i>processedData == processedData<\/i> expression, which doesn&#8217;t make sense because it is always true. Similarly to the rest of the expressions in <i>return<\/i>, the fields of the current <i>this<\/i> object and <i>that<\/i> object had to be compared: <i>processedData == that.processedData<\/i>. This case is one of the typical error patterns found in comparison functions, described in detail in the article &#171;<a href=\"https:\/\/www.viva64.com\/en\/b\/0509\/\">The Evil within the Comparison Functions<\/a>&#171;. So, it turns out that just &#171;a little carelessness&#187; broke the logic of checking the equivalence of objects of the <i>CheckpointStatistics<\/i> class. <\/p>\n<h2>Expression is always true <\/h2>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v6007\/\">V6007<\/a> Expression &#8216;input2.length > 0&#8217; is always true. Operator.java(283)<\/p>\n<pre><code class=\"java\">public static &lt;T> Operator&lt;T> createUnionCascade(Operator&lt;T> input1,                                                   Operator&lt;T>... input2)  {   if (input2 == null || input2.length == 0)    {     return input1;                                \/\/ &lt;=   }    else if (input2.length == 1 &amp;&amp; input1 == null)    {     return input2[0];   }   ....   if (input1 != null)    {     ....   }    else if (input2.length > 0 &amp;&amp; input2[0] != null) \/\/ &lt;=   {     ....   }    else    {     ....   } }<\/code><\/pre>\n<p>  In this method, the analyzer turned out to be more attentive than a person, and reported this in its own peculiar way, pointing out that the expression <i>input2.length > 0<\/i> will always be true. The reason is that if the length of the <i>input2<\/i> array is 0, then the condition <i>input2 == null || input2.length == 0<\/i> of the first <i>if<\/i> in the method will be true. As a result, the method execution will be interrupted before reaching the line with the expression <i>input2.length > 0<\/i>.<\/p>\n<h2>All-seeing analyzer<\/h2>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v6007\/\">V6007<\/a> Expression &#8216;slotSharingGroup == null&#8217; is always false. StreamGraphGenerator.java(510)<\/p>\n<pre><code class=\"java\">private &lt;T> Collection&lt;Integer> transformFeedback(....) {   ....   String slotSharingGroup = determineSlotSharingGroup(null, allFeedbackIds);   if (slotSharingGroup == null)   {     slotSharingGroup = \"SlotSharingGroup-\" + iterate.getId();   }   .... }<\/code><\/pre>\n<p>  The analyzer reported that the expression <i>slotSharingGroup == null<\/i> is always false. This suggests that the <i>determineSlotSharingGroup<\/i> method will never return <i>null<\/i>. Is the analyzer so smart that it could calculate all the values that this method can return? Let&#8217;s double-check it ourselves:<\/p>\n<pre><code class=\"java\">public class StreamGraphGenerator  {   ....   public static final String DEFAULT_SLOT_SHARING_GROUP = \"default\";   ....   private String determineSlotSharingGroup(String specifiedGroup,                                             Collection&lt;Integer> inputIds)    {     if (specifiedGroup != null)     {       return specifiedGroup; \/\/ &lt;= 1     }     else     {       String inputGroup = null;       for (int id: inputIds)       {         String inputGroupCandidate = streamGraph.getSlotSharingGroup(id);         if (inputGroup == null)         {           inputGroup = inputGroupCandidate;         }         else if (!inputGroup.equals(inputGroupCandidate))         {           return DEFAULT_SLOT_SHARING_GROUP; \/\/ &lt;= 2         }       }       return inputGroup == null               ? DEFAULT_SLOT_SHARING_GROUP               : inputGroup; \/\/ &lt;= 3     }   }   .... }<\/code><\/pre>\n<p>  Let&#8217;s go through all the <i>returns<\/i> in order and see what this method can return:<\/p>\n<ul>\n<li>The first <i>return<\/i> results in the argument of the <i>specifiedGroup<\/i>method, but only if it is not <i>null<\/i>.<\/li>\n<li><i>return<\/i> in the <i>for<\/i> loop returns the value of the static final field <i>DEFAULT_SLOT_SHARING_GROUP<\/i>, initialized with a string literal;<\/li>\n<li>And the last return in the method returns the value of the <i>inputGroup<\/i> variable if it is not <i>null<\/i>. Otherwise, the value of the <i>DEFAULT_SLOT_SHARING_GROUP<\/i> field is returned.<\/li>\n<\/ul>\n<p>  It turns out that the analyzer was really able to calculate the impossibility of returning <i>null<\/i> from the <i>determineSlotSharingGroup<\/i> method and warned us about this, pointing out that the<i>slotSharingGroup == null<\/i> check was pointless. Although this situation is not erroneous, however, such additional protection of the analyzer will be able to detect the error in some other case. For example, when a method must return <i>null<\/i> under certain conditions.<\/p>\n<h2>Collect them all<\/h2>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v6007\/\">V6007<\/a> Expression &#8216;currentCount &lt;= lastEnd&#8217; is always true. CountSlidingWindowAssigner.java(75)<\/p>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v6007\/\">V6007<\/a> Expression &#8216;lastStart &lt;= currentCount&#8217; is always true. CountSlidingWindowAssigner.java(75)<\/p>\n<pre><code class=\"java\">@Override public Collection&lt;CountWindow> assignWindows(....) throws IOException  {   Long countValue = count.value();   long currentCount = countValue == null ? 0L : countValue;   count.update(currentCount + 1);   long lastId = currentCount \/ windowSlide;   long lastStart = lastId * windowSlide;   long lastEnd = lastStart + windowSize - 1;   List&lt;CountWindow> windows = new ArrayList&lt;>();   while (lastId >= 0 &amp;&amp;           lastStart &lt;= currentCount &amp;&amp;           currentCount &lt;= lastEnd)    {     if (lastStart &lt;= currentCount &amp;&amp; currentCount &lt;= lastEnd) \/\/ &lt;=     {       windows.add(new CountWindow(lastId));     }     lastId--;     lastStart -= windowSlide;     lastEnd -= windowSlide;   }   return windows; }<\/code><\/pre>\n<p>  The analyzer warns that the expressions <i>currentCount &lt;= lastEnd<\/i> and <i>lastStart &lt;= currentCount<\/i> are always true. And indeed, if you look at the condition of the <i>while<\/i> loop, there are exactly the same expressions. This means that these expressions will always be true inside the loop, so all <i>CountWindow<\/i> objects created in the loop will be added to the <i>windows<\/i> list. There are many options for the appearance of this meaningless check, and the first thing that comes to mind is either a refactoring artifact or a developer&#8217;s playing safe. But it can also be an error if the author wanted to check something else\u2026 <\/p>\n<h2>Incorrect order of arguments<\/h2>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v6029\/\">V6029<\/a> Possible incorrect order of arguments passed to method: &#8216;hasBufferForReleasedChannel&#8217;, &#8216;hasBufferForRemovedChannel&#8217;. NettyMessageClientDecoderDelegateTest.java(165), NettyMessageClientDecoderDelegateTest.java(166)<\/p>\n<pre><code class=\"java\">private void testNettyMessageClientDecoding(        boolean hasEmptyBuffer,        boolean hasBufferForReleasedChannel,        boolean hasBufferForRemovedChannel) throws Exception  {   ....   List&lt;BufferResponse> messages = createMessageList (     hasEmptyBuffer,     hasBufferForReleasedChannel,     hasBufferForRemovedChannel);   .... }<\/code><\/pre>\n<p>  Java&#8217;s lack of the ability to call a method with named parameters sometimes plays a cruel joke with developers. This is exactly what happened when calling the <i>createMessageList<\/i> method that the analyzer pointed to. The definition of this method explains why the <i>hasBufferForRemovedChannel<\/i> parameter must be passed to the method before the <i>hasBufferForReleasedChannel <\/i>parameter:<\/p>\n<pre><code class=\"java\">private List&lt;BufferResponse> createMessageList(   boolean hasEmptyBuffer,   boolean hasBufferForRemovedChannel,   boolean hasBufferForReleasedChannel)  {   ....   if (hasBufferForReleasedChannel) {     addBufferResponse(messages,                        releasedInputChannelId,                        Buffer.DataType.DATA_BUFFER,                        BUFFER_SIZE,                        seqNumber++);   }   if (hasBufferForRemovedChannel) {     addBufferResponse(messages,                        new InputChannelID(),                        Buffer.DataType.DATA_BUFFER,                        BUFFER_SIZE,                        seqNumber++);   }   ....   return messages; }<\/code><\/pre>\n<p>  However, when calling the method, the developer messed up the order of these arguments, which is why the logic of the <i>createMessageList<\/i> method will be broken if the values of the mixed arguments are different.<\/p>\n<h2>Naughty copy-paste<\/h2>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v6032\/\">V6032<\/a> It is odd that the body of method &#8216;seekToFirst&#8217; is fully equivalent to the body of another method &#8216;seekToLast&#8217;. RocksIteratorWrapper.java(53), RocksIteratorWrapper.java(59)<\/p>\n<pre><code class=\"java\">public class RocksIteratorWrapper implements RocksIteratorInterface, Closeable {   ....   private RocksIterator iterator;   ....    @Override   public void seekToFirst() {     iterator.seekToFirst(); \/\/ &lt;=     status();    }      @Override   public void seekToLast() {     iterator.seekToFirst();  \/\/ &lt;=     status();   }      .... }<\/code><\/pre>\n<p>  The bodies of the <i>seekToFirst<\/i> and <i>seekToLast<\/i> methods are the same. What is more, both methods are used in the code. <\/p>\n<p>  There is something screwy here! Indeed, if you look at the methods of the <i>iterator<\/i> object, it becomes clear what error the analyzer helped find: <\/p>\n<pre><code class=\"java\">public class RocksIterator extends AbstractRocksIterator&lt;RocksDB> {   .... }  public abstract class AbstractRocksIterator&lt;....> extends .... {   ....   public void seekToFirst() \/\/ &lt;=   {     assert this.isOwningHandle();     this.seekToFirst0(this.nativeHandle_);   }      public void seekToLast() \/\/ &lt;=   {     assert this.isOwningHandle();     this.seekToLast0(this.nativeHandle_);   }   .... }<\/code><\/pre>\n<p>  It turns out that the <i>seekToLast<\/i> method of the <i>RocksIteratorWrapper<\/i> class was a copy-paste of the <i>seekToFirst<\/i> method of the same class. However, for some reasons, the developer forgot to replace the <i>seekToFirst<\/i> method call in <i>iterator<\/i> with <i>seekToLast<\/i>.<\/p>\n<h2>Confusion with format strings<\/h2>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v6046\/\">V6046<\/a> Incorrect format. A different number of format items is expected. Arguments not used: 1. UnsignedTypeConversionITCase.java(102)<\/p>\n<pre><code class=\"java\">public static void prepareMariaDB() throws IllegalStateException {   ....   if (!initDbSuccess) {     throw new IllegalStateException(       String.format(         \"Initialize MySQL database instance failed after {} attempts,\" + \/\/ &lt;=         \" please open an issue.\", INITIALIZE_DB_MAX_RETRY));   } }<\/code><\/pre>\n<p>  The format strings of the <i>String.format<\/i> method and loggers differ in Java. Unlike the format string of the <i>String.format<\/i> method, where argument placeholders are specified using the &#8216;%&#8217; character, format strings of loggers use a combination of &#8216;{} &#8216; characters instead. This confusion is to blame for this error. As a format string, the <i>String.format<\/i> method is passed a string that was most likely copied from another place where it was used in some logger. As a result, the value of the <i>INITIALIZE_DB_MAX_RETRY<\/i> field will not be substituted for &#8216;{} &#8216; in the <i>IllegalStateException<\/i> message. Whoever catches or logs this exception will not know how many attempts to connect to the database were made.<\/p>\n<h2>Non-normal distribution<\/h2>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v6048\/\">V6048<\/a> This expression can be simplified. Operand &#8216;index&#8217; in the operation equals 0. CollectionUtil.java(76)<\/p>\n<pre><code class=\"java\">public static &lt;T> Collection&lt;List&lt;T>> partition(Collection&lt;T> elements,                                                  int numBuckets)  {   Map&lt;Integer, List&lt;T>> buckets = new HashMap&lt;>(numBuckets);      int initialCapacity = elements.size() \/ numBuckets;    int index = 0;   for (T element : elements)    {     int bucket = index % numBuckets;                                 \/\/ &lt;=     buckets.computeIfAbsent(bucket,                              key -> new ArrayList&lt;>(initialCapacity))             .add(element);    }    return buckets.values(); }<\/code><\/pre>\n<p>  The <i>partition<\/i> method splits elements from the <i>elements<\/i> collection into several segments, and then returns these segments. However, due to the error indicated by the analyzer, no separation will occur. The expression used to determine the number of the segment <i>index % numBuckets<\/i> will always be 0, because <i>index<\/i> is always 0. Initially, I thought that the code of this method was refactored, as a result of which they forgot to add an increase in the <i>index<\/i> variable in the <i>for<\/i> loop. But after reviewing the <a href=\"https:\/\/github.com\/apache\/flink\/commit\/f63f7e6be29709b5871abd055ef1b12476952e75\">commit<\/a> where this method was added, it turned out that this error appeared along with this method. Correct variant of the code: <\/p>\n<pre><code class=\"java\">public static &lt;T> Collection&lt;List&lt;T>> partition(Collection&lt;T> elements,                                                  int numBuckets)  {   Map&lt;Integer, List&lt;T>> buckets = new HashMap&lt;>(numBuckets);      int initialCapacity = elements.size() \/ numBuckets;    int index = 0;   for (T element : elements)    {     int bucket = index % numBuckets;      buckets.computeIfAbsent(bucket,                              key -> new ArrayList&lt;>(initialCapacity))            .add(element);     index++;   }    return buckets.values(); }<\/code><\/pre>\n<p>  <\/p>\n<h2>Incompatible type<\/h2>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v6066\/\">V6066<\/a> The type of object passed as argument is incompatible with the type of collection: String, ListStateDescriptor&lt;NextTransactionalIdHint>. FlinkKafkaProducer.java(1083)<\/p>\n<pre><code class=\"java\">public interface OperatorStateStore  {   Set&lt;String> getRegisteredStateNames(); } public class FlinkKafkaProducer&lt;IN> extends .... {   ....   private static final    ListStateDescriptor&lt;FlinkKafkaProducer.NextTransactionalIdHint>   NEXT_TRANSACTIONAL_ID_HINT_DESCRIPTOR = ....;    @Override   public void initializeState(FunctionInitializationContext context)....    {     ....     if (context.getOperatorStateStore()                .getRegisteredStateNames()                .contains(NEXT_TRANSACTIONAL_ID_HINT_DESCRIPTOR))    \/\/ &lt;=     {        migrateNextTransactionalIdHindState(context);     }     ....   } }<\/code><\/pre>\n<p>  The expression indicated by the analyzer will always be false, which means that the <i>migrateNextTransactionalIdHindState<\/i> method will never be called. How is it that someone looks for a completely different type of element in a <i>Set&lt;String><\/i> type collection \u2014 <i>ListStateDescriptor&lt;FlinkKafkaProducer.NextTransactionalIdHint><\/i>? Without the help of the analyzer, such an error would most likely live in the code for a very long time, since it is not noticeable, and it is simply impossible to find it without a thorough check of this method.<\/p>\n<h2>Non-atomic change of the variable<\/h2>\n<p>  <a href=\"https:\/\/www.viva64.com\/en\/w\/v6074\/\">V6074<\/a> Non-atomic modification of volatile variable. Inspect &#8216;currentNumAcknowledgedSubtasks&#8217;. PendingCheckpointStats.java(131) <\/p>\n<pre><code class=\"java\">boolean reportSubtaskStats(JobVertexID jobVertexId, SubtaskStateStats subtask) {   TaskStateStats taskStateStats = taskStats.get(jobVertexId);    if (taskStateStats != null &amp;&amp; taskStateStats.reportSubtaskStats(subtask)) {     currentNumAcknowledgedSubtasks++;                \/\/ &lt;=     latestAcknowledgedSubtask = subtask;      currentStateSize += subtask.getStateSize();      \/\/ &lt;=      long processedData = subtask.getProcessedData();     if (processedData > 0) {       currentProcessedData += processedData;         \/\/ &lt;=     }      long persistedData = subtask.getPersistedData();     if (persistedData > 0) {       currentPersistedData += persistedData;         \/\/ &lt;=     }     return true;   } else {     return false;   } }<\/code><\/pre>\n<p>  Plus 3 more analyzer warnings in the same method:<\/p>\n<ul>\n<li>V6074 Non-atomic modification of volatile variable. Inspect &#8216;currentStateSize&#8217;. PendingCheckpointStats.java(134) <\/li>\n<li>V6074 Non-atomic modification of volatile variable. Inspect &#8216;currentProcessedData&#8217;. PendingCheckpointStats.java(138) <\/li>\n<li>V6074 Non-atomic modification of volatile variable. Inspect &#8216;currentPersistedData&#8217;. PendingCheckpointStats.java(143)<\/li>\n<\/ul>\n<p>  The analyzer suggested that as many as 4 <i>volatile<\/i> fields in the method change non-atomically. And the analyzer, as always, turns out to be right, because the <i>++<\/i> and <i>+=<\/i> operations are actually a sequence of several read-edit-write operations. As we know, the value of the <i>volatile<\/i> field is visible to all threads, which means that due to the race condition, some of the field changes may be lost. For more information about this, see the diagnostic description.<\/p>\n<h2>Conclusion<\/h2>\n<p>  In Big Data projects, reliability is one of the key requirements, so the quality of the code in them must be closely monitored. The developers of Apache Flink were assisted by several tools, and they also wrote a significant number of tests. However, even under these conditions, the PVS-Studio analyzer was able to find errors. You can&#8217;t completely get rid of errors, but the use of various static code analysis tools on a regular basis will allow you to get closer to this perfect picture. Yes, you read it right, it&#8217;s all about regular use. Only with consistent application, static analysis shows its effectiveness, which is described in more detail in <a href=\"https:\/\/www.viva64.com\/en\/b\/0098\/\">this article<\/a>.<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/533104\/\"> https:\/\/habr.com\/ru\/articles\/533104\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/0df\/283\/b23\/0df283b236b743464ffc2f254f0a6ab6.png\" alt=\"image1.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/0df\/283\/b23\/0df283b236b743464ffc2f254f0a6ab6.png\"\/><\/div>\n<p>  Applications used in the field of Big Data process huge amounts of information, and this often happens in real time. Naturally, such applications must be highly reliable so that no error in the code can interfere with data processing. To achieve high reliability, one needs to keep a wary eye on the code quality of projects developed for this area. The PVS-Studio static analyzer is one of the solutions to this problem. Today, the Apache Flink project developed by the Apache Software Foundation, one of the leaders in the Big Data software market, was chosen as a test subject for the analyzer.  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-414937","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/414937","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=414937"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/414937\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=414937"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=414937"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=414937"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}