Thank you to everyone who has purchased our application and stayed with us as we continue to improve upon things. We are now averaging about 20,000+ push notifications per day and only expect that number to climb more and more. We have much more in store over the coming months and hope you continue to stay with us as we improve our application and systems each day.

20,000+ Push Notifications per Day
October 21st, 2009RSS Viewer: Issues [Update]
October 19th, 2009[Update] We have re-enabled user registrations. However, we will be disabling accounts in which push notifications fail to be delivered. This does not mean that you have to enable push notifications. It only means that if you enable push notifications and we are unable to deliver a message (typically due to invalid devices or uninstalled applications), then we will disable the account temporarily. If your account ends up disabled and you are unable to login, send us an email and we can re-enable the account. Once our next client version is released, we will have better measures in place to avoid unauthorized usage. At that time we will remove these measures.
As many of you have realized, our servers have been having several issues through the past weekend. Unfortunately, are servers are under attack, which we are working to alleviate and resolve. We will most likely be releasing a new client version with a fix in place to stop illegal usage of the application. At that time, you will be required to update your application as we will block all prior versions. This is an unfortunate step back, but we are working to make this as painless as possible while also stopping unauthorized usage.
Thanks,
Nicholas Hagen
RSS Viewer: What’s New
October 15th, 2009As some of you may have noticed, this week we rolled out a new updated server base. While this does not bring you any client side updates (that will be coming soon as we are actively working on completing our next set of enhancements), it does bring you some other notable updates:
- Feeds are now crawled more often resulting in quicker updates to you and less simultaneous updates
- Push notifications more actively and correctly reflect your latest news so that when you login you see the latest news
- Push notifications now lists the site for the particular item
- Push notifications now show both items when two items are pushed simultaneously
- Push notifications now include the number of new articles per site rather than an overall total when three or more updates occur
- Performance and reliability of the server has been dramatically improved. Logging in should be faster, viewing recent items should be faster, viewing articles for a site should be faster, viewing a particular article should be faster, updating your settings, searching, adding sites, etc should all be faster. All in all, everything is just faster letting you get more done in a faster time
As always if you notice any issues or uncover a possible bug, let us know right away so we can quickly resolve it.
Thanks
RSS Viewer
RSS Viewer: Server Improvements and Maintenance Downtime [Online Again]
October 7th, 2009Update [Oct 15, 2009 4:09 AM EST]: Looks like we are back up and running again and things are working much smoother now. If you see any issues, please send us an email right away so we can quickly resolve it (info@znetdevelopment.com). Notifications are currently disabled and under test and should be back live around 9:30 AM EST. Thanks for your patience. Hopefully these server updates will improve the application performance and usage.
Update [Oct 15, 2009 12:14 AM EST]: We will be going down again in 45 minutes (1:00 AM EST) for about an hour or two to move over to our latest server updates. We should have all of the kinks work out and be live soon again working better.
Update [Oct 12, 2009 7:45 AM EST]: Apparently, feeds are not processing properly and a few other issues have arised. We have decided to revert back to the old version while we look into the specific issues. We did manage to improve the performance of loading the recent items, so that fix is currently running. Stay tuned as we hope to get this major server update out in the next day or two.
Update [Oct 12, 2009 6:45 AM EST]: We are back up again and everything should be functional again and things seem more responsive and faster. Once we release our next application update, we hope to improve the performance in that aspect as well and make this one of the fastest RSS readers in the app store. Until then, stay tuned. If you notice any issues, let us know so we can investigate. This was a pretty major server-side update and while we attempt to ensure full functionality, we know that guarantees are never possible. Thanks again.
Update [Oct 12, 2009 1:44 AM EST]: We are now down and should be back up by 3:00 AM EST (7:00 AM UTC/GMT)
Update [Oct 11, 2009 12:15 AM EST]: We are postponing this until Oct 12, 2009 1:00 AM EST in order to do some final verification testing to ensure we get everything working properly. More details coming tomorrow.
This upcoming weekend we will be rolling out another set of server-side changes that should improve performance and reliability even more. This will most likely resolve the remaining issues of login difficulties, timeouts, and inability to get recent feeds or inconsistency between push notifications and recent feeds. We plan to begin rolling these updates out on Sunday, October 11 early morning starting at 1:00 AM ET (5:00 AM UTC/GMT). The expectation is to have a downtime of about an hour. During this time push notifications will not be sent and you will be unable to login. However, once the server is brought back online, you should immediately start noticing drastic improvements. Check back here to get further updates and progress or follow us on Twitter.
We would like to thank everyone for their continued support and for all of our first time buyers in the first month. Once we stabalize our servers and reliability this weekend, we plan to jump right back in to improving the client side interface and our upcoming feature list. We would love your feedback though to help us prioritize which features get in the next release. Add a comment stating your preference including google reader synchronization, dynamic keywords, sharing (digg, twitter, etc), site management (naming feeds), improved language and encoding support, landscape support, and any others you can think of.
Thanks
OneToOne with Hibernate
October 7th, 2009I have a use case within my database schema that involves a one-to-one relationship. Typically, I map most of my tables with an automatic auto-incrementing primary key. The relationship table then has a single foreign key to its single relationship. This works great within Hiberante and HQL without issue. That is until you begin to use Hibernate’s second level cache. Hibernate’s second level cache works by caching all the individual properties and the ids of the foreign key relationships. In other words, it does not store the actual instances themselves. When requested from the cache, Hibernate hydrates the properties into the object form. It then loads the relationships by retrieving from the session (or second level cache) with the foreign key. When you have a OneToOne mapped by a join column such as:
@Entity public class Person { @Id @Column(name="id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; @OneToOne(fetch=FetchType.LAZY, mappedBy="person") private PersonDetails personDetails; } @Entity public class PersonDetails { @Id @Column(name="id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; @OneToOne(fetch=FetchType.LAZY) @JoinColumn(name="person_id") private Person person; }
In this case, Hibernate will cache the person details table with [ id, person_id ]. When it hydrates the cache entry, it knows how to lookup Person based on the person_id cache value. However, when person is cached, it only caches [ id ] since there is no foreign key from person to person_details. As such, Hibernate can only lookup the person_details by using “from person_details where person_id = :id”. You are prolly wondering where I am going with all of this. The point is that even though you are caching both Person and PersonDetails in the cache (or query cache), Hibernate will always invoke a SQL statement whenever person.getPersonDetails() is invoked. This is due to the fact that Person does not have the foreign key relationship forcing Hibernate to issue a query to find it. That can basically kill your database and cache performance.
So what is the proper way to avoid this type of issue? The answer is to use the same key for both tables. Thus, you have a identity auto incrementing primary key on person and then a manual primary key on person_details that is also the foreign key back to person. In the end, they will both always have the same id. When this is done, Hibernate will always know how to go between Person and PersonDetails since the cache value and foreign key relationship is always its own primary key. Hibernate can optimize this and HQL statements as well. To properly do this, you would have the following entities:
@Entity public class Person { @Id @Column(name="id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; @PrimaryKeyJoinColumn @OneToOne(fetch=FetchType.LAZY) private PersonDetails personDetails; } @Entity public class PersonDetails { @Id @Column(name="id") @GeneratedValue(generator="foreign") @GenericGenerator(name="foreign", strategy="foreign", parameters={ @Parameter(name="property", value="person") }) private Integer id; @PrimaryKeyJoinColumn @OneToOne(fetch=FetchType.LAZY) private Person person; }
That tells Hibernate that the relationships are controlled by the primary key. It also tells Hibernate to use the auto-incrementing identity primary key from the database for person and then to use that person’s primary key as the id of the person_details. So, the following statement:
Person p = new Person(); PersonDetails pd = new PersonDetails(); p.setPersonDetails(pd); pd.setPerson(p); getEntityManager().persist(p);
would result in the following SQL:
INSERT INTO person (...) VALUES (?, ?, ...); -- this returns the assigned primary key INSERT INTO person_details (..., id) VALUES (?, ?, ..., X) -- X here would be the assigned primary key of the prior insert statement
So, IMO you should always use OneToOne with PrimaryKeyJoinColumn and ensure the foreign keys are the same as the primary keys. This will improve your cache results and simplify your functionality.
Using “use index” With Hibernate/MySQL
October 7th, 2009MySQL has the notion of specifying a specific index to use when invoking queries in order to override the default provided by MySQL. Typically, MySQL selects the most optimized index based on the query. However, in some cases, it fails to pick properly reducing performance. In those cases, you need to use the keyword “use index” to select the index. For example:
SELECT [FIELDS] FROM my_table TABLE USE INDEX (my_index) WHERE [predicate]
The problem with this is when moving to an ORM-solution such as Hibernate. Being provider-independent, there is no capability natively within Hibernate and HQL to use provider-specific features such as “use index”. However, Hibernate does allow extension points to do everything from adding provider-specific functions to completely rewriting the SQL statements themselves. In this article, I will demonstrate the capability to semi-add “use index” to hibernate.
Before reading take note that this is, IMO, a really ugly hack to make this work. Unfortunately, in my use case, I had the desire to keep my query caches within HQL in line with the timestamps cache created by hibernate for table updates. The easiest way to actually utilize “use index” is to use a native SQL query rather than HQL. Native SQL queries within hibernate can contain any vendor-specific functionality. Further, there may be better ways at doing this, but I had not found any. Thus, this is my hack and so without further ado, let’s get to it.
Hibernate has two extension points that we will use to provide this functionality. The first is a custom dialect used to add specific functions, keywords, etc. The second is an interceptor to intercept various functionality such as preparing SQL statements. When I initially went down this path, I tried to just create and register a custom function in a custom dialect and then invoke it such as:
SELECT [FIELDS] FROM MyTable TABLE useindex(my_index)
However, from what I understand, you may only use custom functions within the where, group, and order clauses, not the from clause. So, I decided to try the interceptor and modify the SQL before getting added to the SQL statement. Unfortuately, Hibernate only passes in the SQL so there is no way to know whether the specified query wanted an index or not. So, I decided to mix the two by using a custom function within a where statement that I could then find in the SQL in order to inject the proper syntax.
The first step here is to create a custom dialect to register our custom function. The easiest way to do this is to just extend the dialect you are already using so that you merely add functionality to your existing dialect. For example, in my persistence.xml file (NOTE: I am using JPA with Hibernate, but this would work with plain Hibernate and session factory configuration as well), I specify my dialect via:
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />So, I just need to create a new class and extend that MySQL5InnoDBDialect. Within the constructor, I just need to register the simple useindex marker function. The result is:
package com.znet.hibernate.dialect; import org.hibernate.Hibernate; import org.hibernate.dialect.function.StandardSQLFunction; public class MySQL5InnoDBDialect extends org.hibernate.dialect.MySQL5InnoDBDialect { public MySQL5InnoDBDialect() { super(); registerFunction("useindex", new StandardSQLFunction("useindex", Hibernate.BOOLEAN)); } }
We use the org.hibernate.dialect.function.StandardSQLFunction class to create a quick function that expects the specified function name and expects a return type of boolean. To use this dialect, just update the configuration to specify the newly created class.
<property name="hibernate.dialect" value="com.znet.hibernate.dialect.MySQL5InnoDBDialect" />This function will in the end take two parameters. The first parameter will denote the table to associate the index to. The second parameter will denote the actual index [eg: useindex(table, my_index)]. This will allow us to mark the resultant SQL with both the index and associated table. Thus, we can properly restructure the generated SQL accordingly. We could now use this function within HQL such as:
String hql1 = "select table from MyTable table where useindex(table, my_index) is true and table.feature is not null"; // result: select [omitted] from my_table table where useindex(table.id, my_index)=1 and table.feature is not null"; // note that hibernate automatically resolves table into table.id within the function
Now that we know what the SQL gets generated as, let’s build an interceptor to transform the SQL. org.hibernate.Interceptors can perform all types of functionality within Hibernate. For our case, we just care about transforming the SQL via the onPrepareStatement(String sql) method. The first step is to create our interceptor. The easiest way is to just extend EmptyInterceptor and merely override the methods you care about. EmptyInterceptor provides empty stubs for all implemented methods.
package com.znet.hibernate.interceptor; import org.hibernate.EmptyInterceptor; public class IndexInterceptor extends EmptyInterceptor { private static final long serialVersionUID = 1L; @Override public String onPrepareStatement(String sql) { while (true) { // check if function specified int idx = sql.indexOf("useindex("); if (idx < 0) { break; } // find end of function int endidx = sql.indexOf(")=1", idx); if (endidx < idx) { throwError("expected useindex(table, index) is true"); } // get both parameters String[] params = sql.substring(idx + 9, endidx).split(","); if (params.length != 2) { throwError("expected 2 parameters to useindex(table, index)"); } // trim parameters and verify String tableId = params[0].trim(); String indexHint = params[1].trim(); if (tableId.length() == 0 || indexHint.length() == 0) { throwError("invalid parameters to useindex(table, index)"); } // find actual table name minus id int dotIdx = tableId.indexOf('.'); if (dotIdx < 0) { throwError("invalid table name in useindex(table, index)"); } // find table name within declaration String tableName = tableId.substring(0, dotIdx); int tableIdx = sql.indexOf(" " + tableName + " "); if (tableIdx < 0) { throwError("unknown table name in useindex(table, index)"); } // remove useindex function from predicate String predicate = sql.substring(endidx + 3); if (predicate.startsWith(" and ")) { predicate = predicate.substring(5); } // inject use index after table declaration sql = sql.substring(0, tableIdx + 2 + tableName.length()) + "use index (" + indexHint + ") " + sql.substring(tableIdx + 2 + tableName.length(), idx) + predicate; } return sql; } protected void throwError(String message) { throw new IllegalStateException(message); } }
There is quite a bit of logic there to go into depth, but the general idea is that it searches for each useindex(table, index)=1 declaration within the SQL statement. For each instance, it tracks back to the table declaration (via from or join) and appends the “use index(index)” statement to the table declaration. Finally, it removes the function from the where statement. So, going back to our example above, we end up with:
String hql1 = "select table from MyTable table where useindex(table, my_index) is true and table.feature is not null"; // before interceptor: select [omitted] from my_table table where useindex(table.id, my_index)=1 and table.feature is not null"; // after interceptor: select [omitted] from my_table table use index (my_index) where table.feature is not null
Before that will actually work, though, we need to register the interceptor with Hibernate. There are multiple ways to do this depending on whether you are using straight Hibernate, Hibernate with Spring, JPA with Hibernate, etc. I will demonstate JPA with Hibernate via persistence.xml, but for other examples, see this article.
<property name="hibernate.ejb.interceptor" value="com.znet.hibernate.interceptor.IndexInterceptor" />That’s it. You can now force the “use index” declaration within any HQL by simply adding useindex(table, index) is true to the where statement. Note that it should be the first statement in the where statement. In theory, although I have not tested this very much, the following complex example should work too:
String hql = "select table from MyTable table " + "inner join table.relative relative, " + "OtherTable other " + "where useindex(table,table_index) is true and " + "useindex(relative,relative_index) is true and " + "useindex(other, other_index) is true and " + "relative.feature1 = other.feature2";
As I stated, this is just an example that I am using and I have in no way tested it to a large extent outside simple examples. If you do take an attempt to use it and find issues, feel free to post a comment and I will look into fixing or helping. Otherwise feel free to use this in any way that helps you.
RSS Viewer: Help Us Rate Our Application
September 27th, 2009If you are a fan of (or even if you are not as we encourage constructive criticism), help us by rating and/or submitting a review in the App Store. Not only does this benefit ourselves in helping to make a better application by knowing what you are most interested in or dislike the most, but it also benefits other users wanting to know more about the application. If you have a blog or other review on an external site, let us know so that we can link to it.
To create a review now, simply click here to launch iTunes to our application page.
Thanks!
Ultimate IE6 Cheatsheet: How To Fix 25+ Internet Explorer 6 Bugs
September 25th, 2009Helpful article to handle IE6 when you have to.
Ultimate IE6 Cheatsheet: How To Fix 25+ Internet Explorer 6 Bugs.
RSS Viewer Down (Now Back Up) for Maintenance
September 24th, 2009We are going down for maintanence at 8:00 AM EST and should be back up by 10:00 AM EST. Bear with us as we improve performance and reliability.
[Update 9:00 AM EST] We are back up and running with some initial performance improvements. Notably, the recent items should load much faster. If you continue to see problems or are unable to login, drop us a note at info@znetdevelopment.com so we can further research the issue. We hope to have more updates and improvements in place tomorrow that should improve performance even more. Thanks for bearing with us.
RSS Viewer Performance Issues
September 22nd, 2009Some of you are noticing severe performance issues and lag time (and possibly some error alert messages) when using RSS Viewer. Unfortunately, our expectations have been exceeded and we are diligently working to improve the server response times and reliability. We expect to have some initial work complete tomorrow that should improve performance and will also be scaling out more over the coming days to really improve performance. As the issues reside at the server level the application will not require an update and you will quickly notice improvements.
If you have other comments, bugs, or suggestions for a future release, drop us a note. We always welcome input, especially criticism. It’s what helps to drive us to do better and meet your needs.
Also, if you are satisfied with the application, make sure to rate the application or add a review in the App Store.
Thanks,
RSS Viewer Development Team