The internet is a hostile place. Publicly accessible machines will be attacked within minutes of being connected, and any unencrypted data in transit is likely to be intercepted and modified. Every day there are successful attacks on applications and databases that are insufficiently protected, resulting in data leaks and ransom demands. In this webinar, RavenDB CEO Oren Eini will go over the threat model for RavenDB, the security aspects involved in deploying in production, and the assumptions involved in working with trusted parties and byzantine partners.
Laurent Kempé asked on DevApps, a French .NET community, about how to validate a form on the first render in ASP.NET Core Blazor. This could be useful, for instance, when you load draft data, and you want to immediately show errors. The default behavior in Blazor is to validate fields when the valu
Yesterday I gave a webinar about Database Security in a Hostile World and I got a really interesting question at the end:If your RavenDB and app are on the same server, would you recommend using certificates?The premise of the talk was that you should always run in a secured mode, even if you are running on a supposedly secured network. That is still very much relevant if you are running on a single server.Consider the following deployment model:(Icons from https://www.flaticon.com/)As you can see, both the application and the server are running on the same machine. We can usually assume that there is no possibility for an attacker not running on the same machine to eavesdrop on the communication. Can we skip encryption and authentication in this case?The answer is no, even for this scenario. That may be a viable model if you are running on your development machine, with nothing really valuable in terms of data, but it isn’t a good model everywhere else.Why is that? The answer is quite simple, there are two issues that you have to deal with:At some future time, the firewall rules will be relaxed (by an admin debugging something, not realizing what they are doing) and the “local” server may be publicly exposed. Even if you are listening only to port 127.0.0.1, without authentication, you are exposed to anything that is local that can be tricked to contact you. That is a real attack.In short, for proper security, assume that even if you are running on the local network, with no outside access, you are still in a hostile environment. The key reason for that is that things change. In two years, as the system grows, you’ll want to split the database & application to separate servers. How certain are you that the person doing this split (assume that you are no longer involved) will do the Right Thing versus the minimum configuration changes needed to make it “work”? Given that the whole design of RavenDB’s security was to make it easy to do the right thing, we should apply it globally.
In version 4.2 we have added an experimental feature to RavenDB, Graph Queries. That was quite a bit of effort and we were really excited about it. The feature was marked as experimental and had been in the product in that state for the past 4 years or so.
Unfortunately, while quite impressive, it didn’t graduate from an experimental feature to a stable one. Mostly because there wasn’t enough usage of graph queries to warrant it. We have seen its usage in some cases, but it seems that our target audience isn’t interested in graph queries for RavenDB.
Given that there isn’t much use of graph queries, we are also aren’t spending much time there. We are looking at the 6.0 release (scheduled around July 2022) and we realize that this feature makes our life more complicated and that the support burden of keeping it outweigh its benefits.
For that reason, we have made the decision to remove the experimental Graph Queries from RavenDB in the 6.0 release. Before we actually pull the trigger on that, I wanted to get your feedback on the feature and its usage. In particular, if you are using it and if so, what are you using it for?
The most common scenarios for this feature are already covered via projection queries in RavenDB, which often can be easier to express for developers.
Regardless, the feature will remain in the 5.x branch and the 5.2 version LTS will support it until at least 2024.
During code review, I ran into the following code (simplified):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Show hidden characters
public class Wrapper
{
private IEnumerable _inner;
public Wrapper(IEnumerable inner)
{
_inner = inner;
}
public dynamic this[int i]
{
get => Enumerable.ElementAt(i);
}
}
view raw
first.cs
hosted with ❤ by GitHub
We have some wrapper object for IEnumerable and allow to access it by index.
I don’t like this code, and suggested this version, instead:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Show hidden characters
public class Wrapper
{
private IEnumerable _inner;
public Wrapper(IEnumerable inner)
{
_inner = inner;
}
public dynamic this[int i]
{
get
{
if(_inner is not ICollection c)
{
_inner = c = _inner.ToList();
}
return c[i];
}
}
}
view raw
second.cs
hosted with ❤ by GitHub
The problem is that if you’ll look at the code of ElementAt(), it is already doing that, so why the duplicate work? It is specialized to make things fast for similar scenarios, why do I write the same code again?
Because I’m familiar with the usage scenario. A really common usage pattern for the wrapper object is something like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Show hidden characters
var wrapper = new Wrapper( item.SelectMany(x=>x.Tags) );
view raw
usage.cs
hosted with ❤ by GitHub
The difference between what ElementAt() does and what I do in the second version of the code is that my version will materialize the values. If you are calling that in a for loop, you’ll pay the cost of iterating over all the items once.
On the other hand, since the instance we pass to ElementAt() isn’t one that has an indexer, we’ll have to scan through the enumerator multiple times. A for loop with this implementation is a quadratic accident waiting to happen.
My first project as a professional software developer was to build a scheduling system for a dental clinics chain. That was a huge project (multiple years) and was really quite interesting. Looking back, I have done a lot of really cool technical things there. I also learned quite a lot from that project. The danger of complexity being one of the chief issues.
Consider a dental clinic, where we have the following schedule for a dentist:
Monday – 10:00 – 15:00
Wednesday – 09:00 – 13:00
Thursday – 11:30 – 16:30
The task is to be able to schedule an appointment for the dentist given those rules.
In addition to the schedule of the dentist, we also have actual Appointments, those looks like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Show hidden characters
{
"At": "2022-03-09T12:00:00",
"Duration": "01:00:00",
"Patient": "patients/2130-C",
"Task": "Filling tooth 26"
}
view raw
appointment-1.json
hosted with ❤ by GitHub
Assume that you have quite a few of those, and you want to schedule a new appointment for a patient. How would you do that? I’m a database guy, let’s see if I can specify the task as a query?
We need a dentist that has availability of a particular length (different tasks have different schedules) and particular qualifications. However, there is no such thing as availability in our model. We have just:
Dentist
Schedule
Appointment
The complexity here is that we need to search for something that isn’t there.
I actually found some of my posts on this topic, from 2006. That isn’t a simple problem. And the solution is usually to generate the missing data and query on that. My old posts on the topic actually generate an in memory table and operate on that, which is great for small datasets, but will fail in interesting ways for real world datasets.
For what it’s worth, RavenDB allows you to generate the missing data during the indexing process, so at least the queries are fast, but the indexing process is now compute-intensive and a change in the dentist schedule can result in a lot of work.
All of that is because of two issues:
We are trying to query for the data that isn’t there.
The information is never used as queried.
These two points are strongly related to one another. Consider how you would schedule a dentist appointment. You first need to find the rough time frame that you need (“come back in six months”) and then you need to match it to your schedule (“I can’t on Monday, I got the kids”, etc).
There is a better way to handle that, by filling in the missing pieces. Instead of trying to compute the schedule of a dentist from the specification that we have, go the other way around. Generate the schedule based on the template you have. The result should be something like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Show hidden characters
{
"Date" : "2022-03-09",
"Dentist": "dentists/3281-C",
"MaximumDuration": "01:30:00",
"Start": "09:00",
"End": "13:00",
"Appointments": [
{
"Start": "12:00:00",
"Duration": "01:00:00",
"Patient": "patients/2130-C",
"Task": "Filling tooth 26"
}
]
}
view raw
schedule-dentist-3281-C-2022-03-09.json
hosted with ❤ by GitHub
In other words, based on the schedule provided, we’ll generate an entry per day for the dentist. That entry will contain the appointments for the day as well as the maximum duration for an available appointment. That means that on query time, we can do something as simple as:
from Schedules where Dentist = $dentistId and At between $start and $end and MaximumDuration >= $reqDuration
And that gives us the relevant times that we can schedule the appointment. This is cheap to do, easy to work and it actually matches the common ways that users will use the system.
This has a bunch of other advantages, that are not immediately apparent but end up being quite important. Working with time sucks. The schedule above is a nice guideline, but it isn’t a really useful one when you need to run actual computations. Why is that? Well, it doesn’t account for vacations days. If there is a public holiday on Wednesday, the dentist isn’t working, but that is an implied assumption in the schedule.
For that matter, you now need to figure out which calendar to use. A Christian and a Jewish dentist are going to have different holiday calendars. Trying to push that into a query is going to be quite annoying, if not impossibly so. Putting that on the generator simplifies things, because you can “unroll” the schedule, apply the holiday calendar you want and then not think about it.
Other factors, such as vacation days, reserved time for emergencies and similar issues make it a lot easier to manage in a concrete form. Another important aspect is that the schedule changes, for any decent size clinic, the schedule changes all the time. You may have the dentist requesting to close a couple of hours early on Monday because of a dance recital and add more hours on Thursday. If the schedule is generated, this is a simple matter to do (manual adjusting). If we have just the schedule template, on the other hand… that becomes a lot more complex.
In short, the best way to handle this is to take the template schedule, generate it to a concrete schedule and operate from that point on.
If you're not already practicing continuous deployment, odds are your team and company would benefit from more frequent deployments. Let's…Keep Reading →
The Main method is the entry point of a C# application. When the application started, the Main method is the first method that is invoked.-- DocumentationIn fact, the Main method may not be the first method of the assembly to be executed when the application starts. There are different methods that
We use cookies to analyze our website traffic and provide a better browsing experience. By
continuing to use our site, you agree to our use of cookies.