07 Jul 2016

База знаний: а стоит ли?

Небольшое вступление

Мир IT - это постоянно движущаяся и развивающаяся структура, все финты и пируэты которой угадать довольно сложно. Технологии и спрос на них меняются не просто во времени, а от команды к команде внутри компании. Из-за этого новичку, да и даже матерому проггеру, порой бывает сложно ориентироваться в происходящем.

Однако основная проблема, на мой взгляд, в том, что в процессе разработки могут возникать проблемы, специфичные для данной конкретной области. О них вряд ли напишут на общедоступных форумах, а если и напишут, то кратко и в такой формулировке, что загуглить это будет непросто выведено опытным путем. Конечно, человеку, который знает область и все технологии проекта будет попроще при возникновении проблемы, а вот новичкам (не только джуниорам, но и просто людям, которые только-только пришли на проект) придется туго. На самостоятельный разбор уйдет довольно много времени, что не желательно, особенно при работе с проектами, требующими от веток отдельных задач быстрого слияния с “боевой” веткой.

Логичный выход - спросить у коллег. Но и это не всегда помогает, и вот три основные причины тому:
1. Высокая загруженность. У команды просто нет времени втаскивать новенького в команду, а потому он обречен болтаться где-то в хвосте, часто исполняя функции “сделай вот эту кнопочку зеленой, а не синей”.
2. “Новая” проблема. Есть такие вещи, которые могут не вылезти и за год разработки. Хотя бы потому, что этот конкретный кусочек не разрабатывался, потребность в нем появилась только сейчас. В таком случае коллеги и не буду способны помочь, так как сами видят такое чудо в первый раз.
3. Человеческий фактор. Новичок в силу особенностей характера может постесняться спросить о чем-либо, может быть, боясь показаться глупым, или навязчивым, или что-то в этом духе.

Соответственно, хотелось бы иметь некий механизм помощи, с которым было бы удобно работать и новичкам, и опытным IT-шникам.

Внутренняя база знаний: кратко о

По сути, база знаний (далее БЗ) - это что-то вроде Википедии, смешанной со StackOwerflow и Habr’ом. В ней аккумулируются внезапно знания о технологиях, используемых на проекте. При этом, это не только полезные статьи о том, как использовать фреймворк или API, это и заметочки о том, как установить нужное ПО, как устранить неочевидные ошибки и т. д., и т. п. Все это оформляется, как правило, в виде web-приложения (можно и настольное сделать, в целом), подключенному к той или иной БД. Туда же можно (и нужно) прикрутить форум, как для простого общения, так и для того, чтобы дать возможность задать вопрос, если нужной статьи в базе пока нет.

Штука хорошая, полезная, но стоит ли игра свеч?

Мысли за

В любой команде разработчиков такие мысли высказывались, я думаю. Приведу пункты, которые мне кажутся наиболее существенными.

Уже третья страница Google…

Существуют довольно специфичные области, разработка в которых ограничена узким кругом лиц (возможно, ваша команда - единственная). Следовательно, найти что-то по теме крайне затруднительно и требует умелой работы с поисковыми системами, что не каждому дано. БЗ же содержит только ту информацию, которая нужна, представлена в понятном Вам виде, а значит, времени на поиск, обработку и усвоение уйдет гораздо меньше.

Вливайся!

Как уже говорилось ранее, новому человеку в коллективе зачастую трудно адаптироваться. Новая область знаний, новые технологии, своя специфика каждой строки кода - поток информации бывает трудно просеять. Конечно же, можно почитать документацию, но где гарантии, что она есть, а если и есть, то хорошо ли написана? Поспрашивать у коллег - это уже освещалось ранее.

Если же существует БЗ, то процесс адаптации в проекте проходит гораздо быстрее. Здесь можно почитать статьи, комментарии к ним, форум, и почерпнуть много всего полезного.

О, у тебя тоже!

Нужно отметить, что порой в проект нужно подключить новую библиотеку или новый модуль. И как всем известно, такое подключение не всегда можно пройти гладко: конфликт версий или с уже существующими модулями, ошибки в самом модуле, некорректная работа с ним и проч. Пытаться решить проблему самому - опять же затратно по времени, значит, думать нужно командой. Предположим, решение найдено. И что с этим делать? Алгоритм может быть путанным, сложным и совершенно не очевидным, понятно, что держать его все время в голове не очень хочется, да вряд ли и не получится. Хочется, все же, где-то это надо записать, чтобы не забыть, а в случае надобности - быстро найти.

БЗ предоставляет такую возможность. Достаточно просто открыть формочку для создания статьи или темы на форуме и поместить туда всю информацию о решении возникшей проблемы. Ура, теперь это никуда не потеряется и всегда будет на расстоянии пары кликов!

Мысли против

Все это, конечно, хорошо, но что-то уж больно ориентировано на сознательного пользователя, да и вообще на человека. А как же все остальное?

А кто работать будет?

Какой бы хорошей идеей не была БЗ, а ее нужно спроектировать. Мало того, что нужно сделать интерфейс и модуль поиска, так еще и структуру БД надо создать. Этим всем должен кто-то заниматься. Можно выделить пару-тройку добровольцев, которые за это засядут, но есть же еще и основной проект. Лезть в overtime? Не хочется как-то ночевать в офисе. Создавать отдельную команду, ответственную за реализацию? Нужно оборудовать рабочие места и выделить деньги за зарплату.

В целом, БЗ несомненно является затратным проектом, требующим упорной работы, времени и денег. С этой точки зрения все упирается в ресурсы, которых у компании может просто не быть.

Нужно больше жести

Исходники, данные, интерпретатор, дополнительное ПО для БД - все это требует места. Всеми правдами и неправдами упихивать это на уже имеющиеся носители - не вариант, там лежат данные основного проекта. Выход есть - закупить еще жестких. Хорошо, а куда же девать бекап? Еще жестких!

Поскольку БЗ подразумевает наполнение, ее исходный, пусть даже небольшой, объем будет неуклонно расти. Будут появляться новые модули, фичи, статьи, темы и сообщения форума, данные о пользователях и проч., и хранить это где-то надо.

Сервер? Какой сервер?

Приложение должно где-то крутиться. Даже если оно настольное, таскать БД на каждую машину не хочется. Значит, надо выделять сервер. Можно подпихнуть его на основные сервера компании, но где гарантия, что все не рухнет от перегрузки? Можно купить дополнительный сервер и развернуть приложение на нем, но сервера-то нынче не дешевые.

БЗ является довольно мощной системой, которой нужны вычислительные ресурсы и оперативная память. Ресурсы, ресурсы и опять ресурсы.

Советы тем, кто решился

Если вы коллективно взвесили за и против и решили сделать у себя БЗ, вот несколько рекомендаций.

  1. Подумайте о БД. Если вы предполагаете, что проблем на проекте будет мало и возникать они будут редко, не нужно создавать монстра, безжалостно поедающего ресурсы ЦПУ и ОЗУ. Подойдите к вопросу рационально, возможно, хватит обычных текстовых файлов, которые будут подгружаться на страницу.

  2. Распределите обязанности оптимально. Не всем в команде нужно бросаться за претворение желаний в жизнь. Подумайте, кому лучше остаться на основных проектах, а кому пойти на БЗ. При этом не стоит сваливать все на одного человека, хорошенько обдумайте и спланируйте распределение обязанностей.

  3. Заранее обдумайте поддержку. БЗ, как и любая система, требует хотя бы минимальной поддержки. Вам придется следить за ее работой, наполнением, возможно, придется что-то дописывать. Лучше всего будет по окончании разработки выделить одного-двух человек, которые будут ухаживать за вашим детищем.

  4. Не запихивайте в БЗ все, что попало. Тщательно фильтруйте информацию, которая будет попадать в БЗ. Решение проблемы “NullPointer на неинициализированной переменной” - это не то, что там должно находиться. Помещайте туда только то, что действительно представляет интерес.

Итоги

Подводя черту под всем выше сказанным, нужно отметить, что БЗ - это полезно и нужно, особенно на больших проектах, когда времени на внезапно появившуюся проблему нет. С другой стороны, ее разработка является весьма трудоемкой, как и дальнейшая поддержка.

Если вы решили создать БЗ - желаю успехов в этом начинании, если нет - тоже успехов, но в чем-нибудь еще!

P.S. Данная статья основана на личном опыте и мнении, конструктивная критика и предложения приветствуются
Read more
23 Mar 2015

AngularJS initial data load + Jersey

Resume: there are several apps I’ve seen developed on AngularJS that seem pretty slow. This apparent slowness comes from the fact that JS developers use REST calls after the first load of the page which is another round trip to the server. So how do we fix this issue?

ng-bind directive

Which means that instead of

<p>{{variable}}</p>
you’re going to use a more verbose syntax:
<p ng-bind='variable'>$defaultValueCameWithHtmlPageServer</p>
The verbosity is not a problem as for me - it looks pretty fine. But what’s more important - you get 2 different ways of putting data into the HTML page: AJAX + REST and also initial JSP/Velocity/Whatever. In both places you may have bugs so 2 things to test and support.

Initially populated JS

But there is a better way described on SO. We create a function that can accept the data:

$scope.initialize = function(json) {  $scope.data = json; };
And then populate this function on the HTML page:
<body ng-controller="MainCtrl" ng-init="initialize( { field: 'value' } )">
So you have to generate the same JSON as you would send back in AJAX response and put it directly into HTML page.

Server Side

One problem left - how to accomplish this on the server side. With Jersey I could come up with this (a big ugly, but working) solution:

StringWriter writer = new StringWriter();
ObjectMapper mapper = new JacksonJaxbJsonProvider().locateMapper(Object.class, MediaType.APPLICATION_JSON_TYPE);
mapper.writeValue(writer, someRestService.getSomeResource().getEntity());
String json = writer.toString();

So in your Controller you trigger the very same REST Service, get its results and convert them into JSON. Afterwards you can pass this variable to the JSP/Velocity/Whatever page.

Read more
17 Jan 2015

Setting javac paths in Ant and Antrun

Resume: article describes how you configure Java compiler in Ant both in a standalone option and as Antrun Maven Plugin. If configured wrong, you may end up with unnecessary forked processes or errors like this:

Unable to find a javac compiler;
 [ERROR] com.sun.tools.javac.Main is not on the classpath.
 [ERROR] Perhaps JAVA_HOME does not point to the JDK.
 [ERROR] It is currently set to "/Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home/jre"

As it appears, there are subtleties which may drive you crazy before you start understanding what’s going on.

Which javac should Ant choose?

There are 2 options Ant can use to compile code:

  • load and use compiler classes from classpath

  • trigger external javac fork (many ways of configuring this)

Using javac classes from classpath

When you specify this option <javac compiler="javac1.6"> it will try to load com.sun.tools.javac.Main and if it’s not found you’ll get: Unable to find a javac compiler. Why wouldn’t Ant find it? If you run Ant as standalone script, it will add $JAVA_HOME/lib/tools.jar to the classpath and the class will be found. But if you wrapped Ant by Maven using antrun plugin, Maven won’t load tools.jar and will directly call Ant classes. Ant cannot load the required compiler class and thus throws the aforementioned error. In order to escape this, you need to add tools.jar explicitly as a dependency to the plugin:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-antrun-plugin</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.sun</groupId>
            <artifactId>tools</artifactId>
            <version>1.5.0</version>
            <scope>system</scope>
            <systemPath>${env.JAVA_HOME}/lib/tools.jar</systemPath>
        </dependency>
    </dependencies>
</plugin>

Note, that potentially some JDK distributions may have this jar in different places. Or may not have it at all (these classes reside in a different jar). Use Maven Profiles with different paths to the dependency set. And then activate those profiles by JDK version or another environment property. Another option - configure this in settings.xml.

How come Ant doesn’t find tools.jar while maven-compiler-plugin finds it without additional tweaks? Maven Compiler uses Plexus Compiler which is just a wrapper that uses standard javax.tools.ToolProvider. This ToolProvider looks for jars using java.home and loads tools.jar on its own. This differs from Ant which simply tries to load com.sun.tools.javac.Main and doesn’t search for any jars.

Change javac using JAVA_HOME

It’s possible to change javac by changing what Java is used by Ant itself. And it will work for both fork and non-fork options, just change JAVA_HOME when you start Ant: JAVA_HOME='/path/to/java/home' ant.

The rest of options are related only to forked javac.

Using System javac

System javac is found by Ant in java.home/../bin path. Which is based on Java you started Ant with. It’s used when Ant is trying to fork an external javac, but the location of javac wasn’t specified explicitly. 2 options to do that: <javac compiler="extjavac"> and <javac fork="true">

Since Ant uses java.home to locate a system JDK, you may be tempted to try changing the property to point to a different JDK (not the one Ant was started with). I wouldn’t recommend it, but it’s still worth mentioning. If Ant is invoked directly by another tool (e.g. it was wrapped by Maven or Gradle), it’s possible in their code or configuration to change system properties like java.home. Note, that it should point to JRE inside of JDK. This is by far the most complicated and subtle way of changing javac in Ant.

Explicitly specifying javac

You can specify what javac to use (and it again will be a fork): <javac compiler="extjavac" executable="/path/to/javac"> and <javac fork="true" executable="/path/to/javac">. This will find javac itself as well as related JDK (e.g. tools.jar). Note, that if you specify executable without extjavac or fork=true, the option will be ignored and you won’t actually change javac.

To fork or not to fork

In many situations it’s vital to restrict resources consumed by processes. CI Server shared between many teams would be one example since you don’t want a build to eat all resources and block other builds. In this case a CI engineer would set the memory limits (Heap, PermGen) in build or CI-wide. And while the original Ant process will use these to restrict its appetite, other processes spawned (forked) by the original build will be able to eat more. Ergo, it’s better not to use forks if it’s possible.

JAVA_HOME vs. java.home

Many threads about javac task talk about JAVA_HOME, I thought it will be appropriate to squeeze in this information here. These variables might be confusing, but they are different things that point to different locations. While JAVA_HOME is an environment variable set by user himself (or installer), java.home is a system property set by JVM during bootstrap and it usually points to $JAVA_HOME/jre. Though it may not. It’s not guaranteed because it’s determined by several factors. More details can be found in JVM code, see init_system_properties_values() function (thanks to this SO post for pointing that out).

Read more
21 Sep 2014

When will programmers grow up?

Do you still think that high-quality programming skills, knowing SOLID principles, writing maintainable tests is enough for you to be called a professional? Let’s elaborate this topic. Don’t start yelling until you make it to the end.

I used to be passionate about high-quality programming, all my attention was drawn by maintainable, flexible and easily readable code. I was reading hell a lot of literature on this - both books and articles. Later on I started to teach others. I was conducting webinars & tech sessions (both at work and on the Internet), writing articles and discussing these topics at JTalks. And I still continue on this, though way less actively.

Good programming skills can kill projects!

That passion was gradually leaving me. First of all because I had another passion now - Continuous Delivery, and second - it started to seem that people who are well-educated in programming can harm projects dramatically. Here is the best example in my career that illustrates what I’m talking about:


At some point of my life I decided to join a startup. It seemed to be a good option to me: they used modern technologies and frameworks and during the interview they asked me about best practices which was a promising sign. Moreover teleworking was really handy to me at that time. So here I am resigned from my regular job for the sake of intriguing startup.

From the first days it started to look wrong: they didn’t have daily meet-ups to be in synch with each other (only weekly meetings which was too rare for that kind of project) and they had a dozen of repos in VCS - too many for the first months of the project! That seemed suspicious, and it didn’t turn better..

It appeared that last couple of weeks were spent for designing domain model and database scheme (wat?). And they were not there yet! I started to be involved in all this designing and I found that every team member suggested their vision of the model. And here comes the discussion.. That discussion was overwhelmed with buzz-words like “Best Practices”, “OOD”, “Performance” and in the end they chose the most complex scheme suggested.

By that time I had experience in Scrum and project management which gave me confidence that things must change for the project to survive. I appealed to the customer who (surprise-surprise!) was an ex-developer with questions and suggestions on improving the current situation:

  • Team communicated once in a week, for that project that meant they worked couple of days and then waited several days for the meeting to share their thoughts. That’s extremely ineffective.

  • They started with distributed storage, complex designing and performance in mind while there was nothing to optimise and refactor yet.

The customer was irritated by the fact that there was nothing ready by that time. But he was too involved into technical details. He was possessed with “we should implement it in a right way!”. To me that was too ineffective for such a small-budget project. So call me a rat, but I left that sinking boat after 2 weeks of struggling. I didn’t take money from the customer, I knew he’d need them in the near future. Couple of years have passed, but on their website I still see ‘Coming soon..’.


That situation was a good lesson to me - they were possessed with the quality and that killed the project, it was so clear that it opened my eyes. That was the opposite to what I learnt and taught. It was a beginning of my own rehabilitation. I started to realise how stubborn and selfish I was in the past.

I recall working for a small company where I was asked to write a tiny project to mix some videos, list them and show them to the users who can vote. The project had a budget for 2 months and I could’ve make it! But I was too blind because of good practices and maintainability. I introduced complex technologies, caching; of course I split the project into different layers and added a lot of extra interfaces for flexibility. Should I say I missed deadline? Surely I did. I was asked why it took me that long, but it was hard for managers to understand the importance of that complexity. Now it’s hard even for myself! That project didn’t require maintainability, extensibility, flexibility or any other “bility”, it didn’t! No changes were made to that project since then. In retrospective I understand that all that crap was needed for myself, I was selfish. I needed to ramp up my skills (which is of course important), but I did it at the expense of missed deadline. And now, when I hear developers criticising managers, I’m careful in supporting the conversation. Because who knows, who knows..

Be moderate in code quality

I don’t want to say that well-written code is bad, I don’t! And neither I defend bad-smelling code. But both extremes are equally bad. Both situation lead to money lost. On one hand it’s possible to end up with a mess that’s expensive to maintain and which leads to lots of nasty bugs. On the other hand if we put too much of thought into design, the project may die before it’s even born!

But you may still say: “Hey, I always write high quality code that doesn’t smell and I always spend weeks on careful designing, but my customer doesn’t go broke! He still is happy with the results!”. Well, that might be true. But first of all, customer doesn’t necessarily live on the edge, it’s pretty common for the customer to have big funds and even if your project doesn’t bring any value, he still might be happy, he just doesn’t notice ineffectiveness! And second - in the majority of cases the costs spent on the project development are much smaller than overall spendings on the business. Even if customer heavily relies on the software you work on, there is still plenty of stuff to care about: marketing, 3d party contracts, client-facing offices and personnel, taxes, transport, et cetera, et cetera. So in the end ineffective development might not be that important for the business.

Then who cares? If I work in a large wealthy company, what’s the big deal if I’m ineffective? Here is my perspective on this:

  • There is a moral concern: if you get paid, then you probably should be worth your money. This is not a big deal for many people though, most of us are selfish.

  • You probably want to get higher on the career ladder. It’s more likely to get there if you try to help and make your company better. And vice versa it’s less likely in case you don’t give a toss about the business of your company.

  • Many of us like thinking of ourselves as professionals. And caring about your product success is another way of being professional. Such people are much more precious to the company than typical I-care-only-about-code engineers.

  • Most of the time we don’t have such a freedom - we’re put into frames, the resources (money, time) are not infinite. And then you start to care not only about quality of the code, but also about delivery and quality of the product. If you put too much accent on code quality, you’re missing deadlines. And it’s usually a big drag.

Professionals don’t write code, they solve business tasks

Speaking about professionalism. It’s weird to see how arrogant today’s programmers are. We’re so self-important, so self-important! (c) It’s very common to hear a programmer mentioning how cool he is and how miserable was that employer he just had interview with. To me that’s a STOP sign. If I were to notice such an arrogant behaviour from someone I interview, I wouldn’t hire him no matter how cool he is technically. Because these people are selfish, they don’t care about the product, they care about coding and getting money from this process. Keep in mind that a businessman doesn’t care about source code - he needs a solution. And if you can’t provide it, then you’d rather stop calling yourself a professional.

I used to work in one out-staff company. There was our team (off-shore) and there was our customer team (on-site). That was a very frustrating job - our off-shore team was much more experienced in programming, really strong team. Meanwhile on-site team was pretty weak technically. But this fact didn’t matter for the customer, on-site team is always more important than off-shore. Why?! Customer spends a lot of money on the maintenance because of these guys, why the heck they are in charge of the project? That’s again because albeit our off-shore team was experienced in programming, we were much weaker in the business we worked for. Only on-site team could talk directly to stake-holders, only on-site team knew what should be the result. Yes, they were writing an expensive-to-maintain code, but in the same time they were solving business tasks! While we simply didn’t have enough strings to pull and business knowledge to put into the work.

So last years it became clear that it’s more important to deliver a successful product rather than use modern tools and have 100% code coverage. And after that I started to notice that people who care about quality most and who use buzz-words like Best Practices are the least efficient overall (I’m not talking about short-term progress, I’m talking about overall!). With their approach projects get overly complicated too soon and it ain’t add up to the readability. And now if I hear this “Best Practices” junk in a conversation, this is a good sign that person doesn’t quite understand the subject, that he hides his lack of knowledge under these words. An experienced programmer uses arguments and facts instead of authority of best practices.

The last point on technical skills vs. business skills: our purpose as engineers is to make money for the employer. This can be done either by reducing costs or by creating new business value. And thus:

  • With poor technical skills and good business knowledge you’re creating a software that makes money

  • With good technical skills and poor business knowledge you’re creating a software that reduces costs, but doesn’t provide as much business value.

And therefore by caring about business you’re adding new value and by being a good programmer you reduce cost. Combine these traits, know when to write good and when to write fast. And be twice as effective!

Q/A

How do I learn new technical stuff if I care about business? Of course in order to be a good programmer, you have to learn new stuff and be up-to-date with technologies. A good rule on this - one new technology in the project is enough. It’s much better to be home with your family than meeting New Year at work fixing bugs because of newly introduced tools.

What if project has budget for years, should I apply all my designing skills to make it maintainable? Just be agile! You can’t design everything upfront. Most of your early decisions will be re-written in the future. Write poor code, get business feedback and then re-write it. Check out Fowler’s best seller: Refactoring: Improving the Design of Existing Code.

Interestingly, business ideas also evolve gradually, they also get refactored! But in order to be purified they need to be checked on real market - that’s where rapid and frequent releases will be very handy and money-raising. And in case you’d want to spend too much time on implementing them.. well, you risk to lose money if your competitor implements it first! And that may outweigh maintainability costs.

Should we all stop hating our managers? I didn’t say that! :) Of course managers should be as realistic as others. But professionals are rare in every field, on any position. So it might be typical for your manager to promise too much to his own higher-ups and unleash the dogs on poor developers. Well, shit happens, I’m not advocating such people. Of course this not a professional behaviour, but maybe.. Maybe the problem lies somewhere below, in the realm of programming. Maybe it’s you who’re playing with the future of your project like a kid sitting at the playground playing with his toys. Grow up, be reasonable in your judgements.

Sponsors

Read more
19 Sep 2014

Prepared Statements: Performance, Security, Tips

NB: in this article we talk mostly about PreparedStatement, but most of the information is suitable for CallableStatement as well.
A lot of us know that JDBC can operate with different kinds of Statements including simple Statement, PreparedStatement and CallableStatement. Let’s talk about their purpose and nuances.

First of all it’s worth mentioning that when databases receive an SQL query they check its syntax, parse it (soft parse), optimise it (hard parse) and create a query plan for it. Query Plan is a DB-level instruction on how exactly it’s going to execute the query.

DBs can cache queries they execute, thus if we’re doing something like this: select * from books and then invoke it several times, then at some point database will start caching it. In most systems a cache is just a map, which means it has a key and a value. In our case the key is the query (well, its hash) and the value is the Query Plan. When DB receives next identical query, it checks whether there is a Query Plan already prepared waiting to be fetched from the cache. If it’s there, then database won’t need to parse it again and we’ll get a performance boost (though it still re-builds the Query Plan in some cases when it wants to optimise the query even more).

It’s worth noticing one more time that the key in the cache map is a query. This means that this SQL: select * from books where id=1 and this one: select * from books where id=2 are different, they both will be compiled. Thus we can’t cache the same query just because it has different parameters! Well, that’s exactly the case PreparedStatement can cope with. It’s possible to rewrite the query to: select * from books where id=?. Then each time we want to execute it, database will take the Query Plan from the cache, the only thing left is just to pass parameters which will replace the question mark. Of course every database wants to be the best and it works with cache in its own way: some of DBs might invalidate cache entries quickly, others will wait longer, the decision may depend on statistics after all.
Now what happens when we use JDBC:
1. We start with connection.prepareStatement("some query"), JDBC Driver asks database to prepare the statement*. DB answers with the identifier of the query (most probably - its hash) and additional data like number of params in the query.
2. Next we invoke executeQuery() and JDBC Driver sends the identifier of the query and params. Database finds the query by its ID and simply uses its Query Plan without the need of parsing the query again.

Some of you may notice, that next time we invoke connection.prepareStatement() there will be another communication to the database in order to prepare the query, and then yet another communication for actual query execution. First of all there are 2 extra network requests which is bad from performance point. Second, why the heck do we prepare it again while we already have query ID? Wouldn’t it be simpler to reuse the same PreparedStatement? Well, that’s exactly what happens under the hood!

Connection#prepareStatement(String sql) - this is where magic takes place. JDBC Driver checks the object in its internal cache (not a DB cache!) by the query we passed to the method. And if it’s there, then old PreparedStatement is returned. If not, a new object is created**. This is called implicit caching***.

Next stop: PrepareStatement#close() - this method doesn’t quite stand for its name, it doesn’t actually close the statement but rather places it to the internal cache. Physically PreparedStatment gets closed only in cases a) if Connection is closed b) when cache reaches its max capacity and we need to empty it from old and rarely-used statements c) if cache is switched off d) if cache is not supported by JDBC Driver.

Finally, here are some points related to MySQL (well, most of the stuff will still be common to other databases):

  • SQLs have to be identical (queries with words USERS and users are different!) - that’s true for all the databases****

  • PreparedStatements are not always cached the first time they’re executed, sometimes you need to query database multiple times.

  • Connections to different MySQL Servers, or Connections using different protocols, or even two Connections with different encodings - they all will use different caches.

  • Query shouldn’t start with spaces (well, to be true I’m not quite sure on this, but I’m tired of reading docs already :) For PostgreSQL this is true).

  • Sub-queries and queries with UNION are not cached.

  • Queries inside stored procedures are not cached.

  • MySQL Server < 5.1.17 doesn’t cache the queries, higher versions have their own “ester eggs” which sometimes do not allow caching the queries, so read docs carefully!

  • You should set cachePrepStmts to true, it’s switched off by default. Use connection params like prepStmtCacheSize and prepStmtCacheSqlLimit for MySQL configuration.

Security benefits of Prepared Statements?

Besides performance-related features PreparedStatements secures us from SQL Injections. In order to make explanation short, the example will be silly. Let’s say we have a forum engine and functionality “Remove User”. We specify the username on UI and Submit the form. On the back end we have a code like this:

String query = "delete from users where username=" + username;`

You should remember for your entire life - this is nasty! If a bad guy would pass on UI something like this: smith ' or 'a'='a then we’ll get such a query: delete from users where username='smith' or 'a'='a'. Because a always equals to a our where statement will always be true for all the records in the table. And all of them will be deleted. In order to be safe in this case we would need to escape the string. This means that all the symbols that are meaningful for the database (like quote symbol) should be replaced with some other char sequence. If you do it yourself, it would look like this:
delete from users where username='smith\' or \'a\'=\'a'

Because all the quotes are replaced with \’ which means ‘interpret the quote as a string, not as a command’, we’re safe now. But bad guys have a large arsenal, they will always win unless you have a bullet-proof solution. PreparedStatement is is this solution. Because in case of PreparedStatements queries look like this: delete from users where username=? and we don’t construct queries with parameters ourselves: preparedStatement.setString(1, username), we’re protected from any kind of SQL Injections - all the escaping is managed by the database itself.


* JDBC Drivers that don’t support pre-compilation (Prepared Statements) send queries only on the executeQuery() step.
** Notice that when we create usual Statement we don’t pass strings which means every time a new object is instantiated.
*** Actually some JDBC Drivers (like Oracle) can cache usual Statements as well. In case of Oracle JDBC Driver you’d need to work with implementation-specific API and it still won’t be that effective. That’s called Explicit Caching.
**** Of course I didn’t look at every single database, but that’s true for 3 of the most popular drivers I’ve looked into.

Read more
07 Aug 2014

A collection was no longer referenced by the owner

The full error message looks like this:

org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" 
was no longer referenced by the owning entity instance: 
org.javatalks.training.hibernate.entity.bag.Book.comments`.

The error says that Entity had a Collection with all-delete-orphan configured. Then instead of changing the original Collection a brand new Collection was set. Here is the code that’s responsible for handling such cases in Hibernate (class org.hibernate.engine.internal.Collections):
if ( e != null && e.getStatus() != Status.DELETED && e.getStatus() != Status.GONE ) {
	throw new HibernateException(
			"A collection with cascade=\"all-delete-orphan\" was no longer referenced by the owning entity instance: " +
			loadedPersister.getRole()
	);
}

The code intentionally checks whether the Collection is referenced by any Entity or not (in case the Owner was removed). Unfortunately code doesn’t explain why this is a problem - this is probably a way to ensure the user didn’t accidentally overwrite the Collection. Anyway the correct way of removing Collections or their items is to use remove()/removeAll(). Here are 2 cases you can reproduce the error (in Groovy):

Collection is overwritten and the Owner gets updated

@Test
    void 'when updating, if orphan collection is dereferenced, exception thrown'() {
        List<Comment> comments = [new Comment(body: "comment1"), new Comment(body: "comment2")]
        Book original = new Book(title: "with comments", comments: comments)
        bookDao.save(original).flushSession()

        try {
            //it's not allowed to replace collection with cascade=orphan
            original.comments = [new Comment(body: 'new')]
            bookDao.saveOrUpdate(original).flushSession()
            assert false
        } catch (HibernateException e) {
            assert e.message.startsWith('A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance')
        }
    }

Owner gets merged, but new Collection is null

This is a tricky one. Usually if we merge a detached Entity the collection is updated correctly - those elements that are now absent - they get deleted from resulting Owner. But if new Collection is null..

@Test
    void 'when merging, if new collection is null, exception thrown'() {
        List<Comment> comments = [new Comment(body: "a"), new Comment(body: "b")]
        Book original = new Book(title: "with comments", comments: comments)
        bookDao.save(original).flushAndClearSession()

        try {
            bookDao.merge(new Book(id: original.id, comments: null))//exception only due to null
            bookDao.flushSession()
            assert false
        } catch (HibernateException e) {
            assert e.message.startsWith('A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance')
        }
    }

Read more
06 Aug 2014

Nexus: why you shouldn't use LATEST

In every team where I happened to work with Sonatype Nexus I’ve seen the same misunderstanding on what LATEST version of artifacts is. People didn’t always understand why they shouldn’t use it.

Every artifact has a meta file maven-metadata.xml, which looks something like this:

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-javadoc-plugin</artifactId>
  <versioning>
    <latest>2.9.1</latest>
    <release>2.9.1</release>
    <versions>
      <version>2.8.1</version>
      <version>2.9</version>
      <version>2.9.1</version>
    </versions>
    <lastUpdated>20130628064237</lastUpdated>
  </versioning>
</metadata>

Now let’s imagine you want to download the latest version of the artifact. Of course first links in google will lead you to the REST query: http://somerepo.org/service/local/artifact/maven/redirect?r=central-proxy&g=groupId&a=artifactId&v=LATEST And maybe, just maybe, it will work for some time. But later at some point Nexus will start returning old artifacts.

This is because when Nexus searches for LATEST first it checks whether maven-metadata.xml has <latest> and chooses it. If this tag is absent (which is usually true at first), then the last item in <versions> is used.

This mechanism meets your expectations for some time because at the beginning maven-metadata.xml doesn’t have <latest>. Versions are added to the end of the list as new artifacts get uploaded which means that the latest version is returned. But then one of these happens:

  • Someone/something puts <latest> to the meta file.

  • Developers work in branches and several teams independently upload artifacts with different versions. The last upload is the winner - one of artifacts will be listed in the end of <versions> and therefore it will be chosen as LATEST.

As it appears in Nexus LATEST works correctly only for plugins and this mechanism MUST NOT be used for casual artifacts.

Couple of Q/As:

Who updates maven-metadata.xml?

Albeit many think that Nexus is the one, it’s actually Maven Deploy Plugin who does the trick: it first fetches existing maven-medatadata.xml from remote repo, then it updates all the required information and uploads it back to the repo. If the file doesn’t exist in the first place, then it gets created and uploaded.

How come <latest> shows up if Maven doesn’t use it?

Maven doesn’t actually add this tag. Nexus has its utility mechanisms, one of them is Rebuild Metadata. This item can be found in the context menu of the repositories and folders. It removes existing maven-metadata.xml, then it iterates over the artifacts and creates a brand new meta file according to what it found. And this is when <latest> is added. What for? I wish I knew. I even noticed <latest> in cases no one (presumably) ran Rebuild Metadata, thus one may assume there are other events that trigger these updates. Note that during Rebuild the versions are sorted according to the internal algorithms of Maven, these algorithms are then used by Nexus:

The ordering is derived by parsing the version string and supports sematic versioning with additional semantics for specific classifiers. Further details can be found in the documentation for the implementing class GenericVersionScheme.

This means that what was LATEST before may not be latest anymore.

Why <latest> is not always up-to-date?

Even if you happened to start Rebuild Metadata, this is still a one-time action. Afterwards it’s Maven who updates meta files. And it won’t do anything with <latest> - Maven will simply copy it from the maven-metadata.xml originally obtained from Nexus.

Let’s sum up

If you need a mechanism to fetch the latest version of the artifact, you MUST implement it yourself (presumably you’ll write a script). Those out-of-box mechanisms Nexus provides will simply stop working at some point.

Also note, that if you think you need the latest versions, you’re probably moving in a wrong direction, I haven’t seen good workflows which require latests.

PS: the links on Sonatype Wiki are now broken, they probably moved their pages or removed them from public access. URLs will be updated when I find new locations.

Read more
06 Aug 2014

Spock Lifecycle and non-intuitive WHERE block

Résumé: as it appeared Spock’s Lifecycle is not always intuitive, this is especially true for where block. Even though it’s located inside of a test method, its execution happens outside and what’s even more peculiar - it’s invoked even before setup() happens or fields get initialized.

Spock allows you writing data-driven tests in a very easy manner using where:

def "square function"(int a, int square) {
        expect:
            square == a * a
        where:
            a | square
           -1 | 1
            0 | 0
            1 | 1
}

But docs keep silence on how this magic is implemented. Spock’s Syntax is not pure Groovy, its code gets transformed with Groovy AST. Depending on the result of these transformations we may or may not use some features of Spock.

First, where is going to be factored out into a separate method within the same test class. But what’s really interesting - it will be invoked before setup(). The whole thing is here:

This means that variables that are initialized in setup() won’t be seen in where:

class SomeTest extends Specification {
    int a = 2
    int b = 1

    def setup() {
        b = 1
    }
    def someTest(a, b){
      expect:
        b != a
      where:
        a << [a]
        b << [b]
    }
}

In this test we’ll get a == b == 0 which demonstrates the point.

As a consequence all the JUnit Listeners (after all Spock is just an extension of JUnit) that observe beforeTestMethod will also be triggered after the data is initialized in where. This includes DependencyInjectionTestExecutionListener from Spring TestContext. Which means that if you want to use @Autowired fields which are injected from the context, you won’t be able to use them in where, what you’ll get there is null.

Read more
11 Jul 2014

Корпоративные стандарты или Мама анархия?

Резюме: многие компании (а часто и команды) в какой-то момент пытаются выработать свои соглашения и стандарты по строению и оформлению кода/документов, процесса разработки, использованию инструментов и пр. В большинстве же ситуациях этого делать не нужно. Но если нужно, то как?

Стандарты и Соглашения (СиС) можно разделить например так:

  • СиС по написанию кода

  • СиС по процессу разработки и доставки

  • СиС по оформлению UI (UI приложений и документов)

Некоторые, возможно, подумают что СиС пропорциональны размеру компаний, мол, чем она крупней, тем больше “бюрократии”. Чтоб развеять сей миф, вот несколько примеров с моих мест работы:

  • Место1 (~60K человек, ~15K приложений). Есть СиС по UI документов и приложений, по возможным фреймворкам и библиотекам, а также по тому как должен выглядеть конечнный бинарник.

  • Место2 (похожие цифры: ~60K человек, ~15K приложений). Есть СиС только по оформлению документов.

  • Место3 (>1K человек, ~20 приложений). Есть СиС по: Оформлению документов, Инструментам и процессу сборки, Процессу проверки артефактов, Версионированию приложений, Версионированию баз данных, Хранению кода, Конфигурированию приложений, Процессу релиза.

Корпоративные СиС - вредители

Усложняют введение новых людей. Т.к. корпоративные стандарты отличаются один от другого в разных компаниях (командах), то новый человек прийдя к вам в проект, будет вынужден тратить усилия на то, чтоб влиться в процесс. Вам прийдется объяснять ему как что нужно делать, затем проверять. Ну и для упрощения процедуры конечно нужно описать ваши СиС.

Разным командам подходит разный стиль. Разработка у многих - творческий процесс, да и посредники современных методологий (Agile) утверждают что процесс нужно подстраивать под свои конкретные нужды, не усложняя его без реальной необходимости. Поэтому создав корпоративный стандарт, которому должны подчиняться все, вы скорей всего ухудшите эффективность конкретных команд и/или личностей, этот процесс им будет просто не подходить.

СиС сложно внедрять. На свете есть очень много упрямых людей, каждый из которых считает свое мнение самым весомым и свое виденье - самым верным. Если они будут не согласны с вашим мнением, вам прийдется потратить уйму усилий на переубеждения. А еще у команд есть свои сроки за несоблюдение которых можно поплатиться деньгами, у вас должно быть достаточно обоснований и авторитета чтоб увеличить нагрузку на без того опаздывающую команду.

Стандарты нужно поддерживать. Это большой, и что тоже важно, - нудный кусок работы. Найдутся люди, которые не до конца поймут ваш документ, либо в нем найдутся баги, несовместимости с некоторыми ситуациями. А еще - через год у вас может сложиться другое мнение, стандарты прийдется обновлять, а затем заново внедрять.

Стандарты меняются с людьми. Сейчас эти СиС разрабатываете вы и все идет своим чередом, но вы уйдете и на ваше место прийдет кто-то другой с мнением, отличающимся от вашего. И вся катавасия вероятно начнется сначала. Стоило ли оно того?

Оправдания-неудачники для стандартов

Чтоб все унифицировать. Это плохой довод, скорей всего какому-то четкому пацанчику, кто прыгал между проектами и завоевал авторитет у руководства, захотелось сделать их похожими. Тем не менее унификация не должна быть самоцелью. Нужно унифицировать, потому что… И только если определили почему, стоит задумываться о том чтоб тратить на это усилия.

Чтоб проще было ротировать людей. Это как правило тоже неудачный довод (хотя по-моему встречается чаще всего). Во-первых, люди часто сами хотят ротироваться чтоб сменить обстановку, попробовать новые подходы, чтоб скучно не было. А тут вы со своей унификацией. Во-вторых, повторюсь, проекты разные и требования к ним тоже могут быть разными. Если вы, к примеру, унифицируете используемые фреймворки и технологии, вы можете сильно усложнить эффективную разработку, например, будут проблемы с производительностью или архитектурой, народу прийдется вставлять костыли только ради унификации.

Оправдания-удачники для стандартов

Интерфейс между командами. Разработку могут поддерживать несколько команд, например, вашими окружениями заведует отдельная DevOps команда, или ваше приложение тестирует отдельная QA команда, или релизят продукт какие-то левые люди, или кто-то для вас настраивает CI, или… Для таких команд вы можете быть не единственными, они могут изменять вам еще с 10ю проектами. И если каждый из этих проектов - уникальный, то такие команды-сервисы будут страдать и работать неэффективно. Т.е. будет разумно если такая команда напишет документ, которому будут следовать все остальные. Как пример, CI команда может потребовать чтоб все работали только с Maven.

Чтоб продукты были похожи для клиентов. Иногда да, есть требование, чтоб продукты были похожи с точки зрения UI, чтоб пользователи понимали, что это один и тот же поставщик или один и тот же ресурс.

Уменьшить кол-во поддержки в будущем. Если разные команды владеют одним инструментом (Nexus, JIRA, CI, etc.) так может случиться, что все они по-разному его настраивают и в какой-то момент произойдет коллапс. Чтоб этого не произошло, либо у каждой команды должны быть свои инструменты, либо стоит задуматься над общими соглашениями.

Имеется экспертиза. Допустим есть ИнструментА, которым на очень хорошем уровне владеют N людей. И есть возможность использовать альтернативный ИнструментБ, о нем вчера вахтерша рассказала. Понятно, что любой инструмент не идеален, но учитывая обстоятельства использовать ИнструментБ - значит повышать риски. Да, нам хочется разнообразия. Да, истину постигают в сравнениях. Конечно имеет смысл поэкспериментировать если этому способствует обстановка, вдруг ИнструментБ окажется намного удобней. Но важно не переборщить, один новый инструмент/фреймворк на проект - достаточно, чтоб не забыть о сладостном геморрое. За сим, если у вас есть экспертиза в компании, логично если все команды будут использовать по возможности ИнструментА.

Какими должны быть стандарты

Если уж вы решились стандартизировать что-либо в своей конторе, то вот несколько советов.

Возможности не следовать стандарту быть не должно. Для этого здорово если все проверки автоматизируются. Хотите, чтоб commit message начинался с Issue ID? Сделайте hook который будет проверять формат и отвергать коммит если он не соответствует стандарту. Код должен следовать соглашениям? Настройте проверки, которые будут валить сборку если правила не соблюдены. Если же стандарт существует только на словах/в документе, то всегда будет находиться кто-то, кто ему будет не следовать. Это чревато тем, что народ будет тратить время на разбирательства и пояснения, а делать это не захочется уже на 3ий раз. В какой-то момент начнут забивать все.

Отдельно тут хотелось бы отметить логирование потраченного времени. Это тоже своего рода соглашение-стандарт относящийся к процессу, когда руководитель проекта к примеру просит всех отмечать сколько времени было потрачено и на что. Почему это неудачная затея можете почитать тут.

В данном случае критерием можно сделать следующее: если команда не будет придерживаться установленных правил после того как инициатор ушел в отпуск, значит стандарт не был внедрен успешно или эти правила не поддаются внедрению и пробовать этого даже не стоит.

Способы автоматизировать проверки:

  • VCS hooks

  • Maven Enforcer Plugin

  • PMD

  • Доп. проверки на CI сервере

Нужно иметь возможность обойти ограничения. Это противоречит предыдущему пункту, но иногда, в каких-то критических ситуациях, все-таки полезно иметь обходной путь. Вот знать его ВСЕМ не обязательно :)

Обратная связь от автоматизации должна быть информативной. Т.е. если какое-то правило нарушено и проверка не прошла, сообщение об ошибке должно содержать пояснение, предложения (как исправить ситуацию), возможно ссылки на доп. ресурсы и, на худой конец, контакты людей кто может проконсультировать.

Документы должны легко читаться. Они должны быть небольшими (максимум - пара страниц) и простыми, желательно в неофициальном стиле. Это кстати часто не соблюдается, народ любит позаковыристей написать, особенно если за этот документ нужно будет отчитываться перед начальством. Не будешь же объяснять начальству почему ты потратил 2 дня на документ в котором есть смайлики. Однако нужно понимать кто ваша целевая аудитория и подстраиваться под них, а не под руководство.

Для разработки и поддержки стандартов нужны выделенные люди. Это если вы реально хотите чтоб им следовали. Потому что работы тут очень много, вполне возможно что таких людей должна быть целая команда. Если же вам следование стандартам не критично, см. следующий пункт.

Гайдлайны - выгодней стандартов. Гайдлайны отличаются от стандартов необязательностью. Вы можете описать какие-то требования к проектам/процессам, но лишь как советы, мол, так будет лучше и проще для всех, следовать им желательно, но не обязательно. Так, например, поступают Atlassian, у которых есть UI гайдлайны. Большинство, стоит заметить, им следуют, даже учитывая что следование этим правилам никто не проверяет.

Используйте официальные практики инструментов. Проще не писать свои документы, а использовать уже описанные соглашения на сайте самого инструмента. Такие соглашения разработчики смогут использовать и на других проектах и в других компаниях. Это хороший способ стандартизировать везде.

Плюсы в таком случае в том, что замарачиваться над внедрением этих правил нужно будет намного меньше. И по идее многие будут им следовать хоть в какой-то мере без пинков.

Выводы: как бы некоторым не хотелось все унифицировать - сделать это до конца может далеко не каждый. Поэтому либо разрабатывайте гайдлайны, либо готовьтесь посвещать унификации все свое рабочее время, либо готовьтесь к тому что следовать вашим стандартам и соглашениям будет небольшая группа людей.

Read more