On 22/03/12 12:00, Matthew Toseland wrote:
> On Wednesday 21 Mar 2012 00:30:42 Ximin Luo wrote:
>> "Implicit dependency" means that changing some code in one area results in a
>> logical error in the other, that is not immediately apparent in the
>> documentation nor any compile-time checking built into the language. 
>> Obviously
>> this is not necessarily a bad thing, but there is a lot of it in the config
>> system, and elsewhere, including in how the configured values are used.
> 
> Okay...
>>
>> For example, for the longest time there was no centralised management of
>> run-time files used by Freenet, and even now they are just a bunch of extra
>> fields in Node.java because I had no existing structure to fit such a system
>> into. However, Node.java is such a fucking disorganised mess, with several
>> hundred fields, that it's not immediately obvious to everyone that they 
>> should
>> be using "new File(Node.nodeDir(), fileName)" rather than "new 
>> File(fileName)".
>> I cleaned it up when I first wrote that part, but it looks like misuses have
>> slowly crept in again.
> 
> So your complaint is that there is no single central point to put all the 
> folder names in? Feel free to create one.
> 
> Making stuff static is tempting but means we can't do multi-nodes-in-one-VM 
> tests, although I believe classloader hacks can solve that? Is there much of 
> a performance cost? Otherwise we can just use a global object and grab it 
> from Node, or ClientContext.
>>
>> Yes, configs have EVERYTHING to do with "writing the values out". All the
>> read/write logic is wrapped up into a ConfigCallback (which throws a
>> InvalidConfigException) which is ONLY PASSED TO THE CONFIG SUBSYSTEM. Even
>> ignoring the obvious intention of whoever named these classes, the target of
>> the read/write logic is completely detached from the logic itself.
> 
> ConfigCallback doesn't do any writing out. And the getters don't throw. It 
> can only throw when a value is changed. So it's still not clear what the 
> problem is.

Irrelevant. To repeat:

- the target of the read/write logic[1] is completely detached from the logic
itself[2], and too strongly-coupled with the config system[3]
- too much implicit dependency, due to the way the variables are poorly managed

[1] whatever get/set acts on
[2] ConfigCallback
[3] via .register(), so only the config system sees it

Who said anything about making stuff static? Stop making up random straw mans.

"Feel free to create one" - stop making excuses for not cleaning up your shit
and telling someone else to do it when they complain! Node.java is a complete
fucking mess. To make any sort of progress in having a well-structured config
system, that needs to be cleaned out first.

>>
>> Your explanation about the ObjectContainer also assumes a severely stunted
>> events framework can't support fine grained thread control. Why can't there, 
>> in
>> your imagination, exist SOMEWHERE, a framework that lets you mandate "only 
>> run
>> jobs of type J in thread T"?
> 
> And those jobs are called with parameters (ObjectContainer, ClientContext) ? 
> Since we'll have to subclass it anyway, it's going to be more messy than the 
> current solution, isn't it? Anyway DBJob/DBJobRunner is pretty simple 
> (although it's hidden in NodeClientCore along with ClientContext 
> initialisation which is kind of messy).
> 

This is bullshit, there is already a "DBJobWrapper implements Runnable" which
needs nothing extra passed in. In the general case, there are *always* ways to
get around fiddly crap like this, and they are much simpler than WRITING YOUR
OWN EVENTS FRAMEWORK (and logging framework and config framework etc etc etc).

> I agree we should make *Executor use the standard APIs though - while keeping 
> the ability to change thread priorities.
>>
>> An events framework could handle errant plugins by setting a timeout for all
>> blocking jobs. If they don't complete within X time, interrupt the thread
>> and/or kill it if it doesn't respond to the interrupt. If the plugin can't
>> handle it, tough shit their fault. (This is how certain companies run 
>> reliable
>> services on a massive scale.)
> 
> Can't be done. Period.
> 
> It's Java. Java does not support killing threads. According to the 
> documentation the reason for this is that killing a thread might cause 
> synchronization problems. Which is true, but I would have thought it was 
> solvable ... Anyway if you need to be able to kill stuff it has to run in *a 
> separate JVM*.

Then interrupt is fine and covers most cases.

>>
>> As for the updater, simply seeing where the currently-running JARs are, is 
>> not
>> enough, if an update introduces extra JARs as a dependancy. It's necessary to
>> have an explicit list of all the dependencies. Unfortunately it's not enough 
>> to
>> simply use the Class-Path attribute of the manifest of freenet.jar, because
>> that hard-codes the path to the other JARs, whereas we need it to be variable
>> for packaging purposes. So, this information can only be figured out at
>> *package time* (for arbitrary package layouts), and the updater needs a
>> mechanism to read this information, and probably update that as well.
> 
> You are planning to have a packaged version which is able to 
> auto-update-over-Freenet?
> 
> Currently class-path hardcoded freenet-ext.jar for convenience more than much 
> else. 99% of installs use the wrapper.

Any software package should support being installed into any arbitrary layout.
It's just good practise.

>>
>> X
>>
>> On 20/03/12 23:18, Matthew Toseland wrote:
>>> On Monday 19 Mar 2012 01:52:19 Ximin Luo wrote:
>>>> On 16/03/12 18:13, Matthew Toseland wrote:
>>>>> On Thursday 15 Mar 2012 21:02:26 Ximin Luo wrote:
>>>>>> (Top-posting because previous post is too long to reply to in-line.)
>>>>>>
>>>>>> ## refactoring
>>>>>>
>>>>>> Refactoring the code helps to make it more understandable for other 
>>>>>> coders. One
>>>>>> of the reasons why I don't work more on freenet myself is because it's
>>>>>> extremely difficult to make any changes that are more than a simple bug 
>>>>>> fix.
>>>>>
>>>>> Hmmm, good point. But from FPI point of view, unless it's a very quick 
>>>>> and high impact refactoring,  it's probably too long term.
>>>>>>
>>>>>> When I was writing code for the config subsystem to separate out 
>>>>>> different
>>>>>> files into different directories, this was very frustrating and took up 
>>>>>> much
>>>>>> more time than I expected.
>>>>>
>>>>> I'm still curious as to why.
>>>>
>>>> The code is spread out over many files not related to config management; 
>>>
>>> It is? How so?
>>>
>>> The code for handling the config files simply takes a value and applies it 
>>> to that subsystem. Short of converting it to get*/set*'ers, using 
>>> reflection, I don't see how we could make it much simpler.
>>>
>>>> this
>>>> creates lots of implicit dependencies, which is not directly apparent in 
>>>> the
>>>> syntax/structure of the code nor the semantics of the languag. In other 
>>>> words,
>>>> spaghetti code.
>>>
>>> I still don't follow - implicit dependancies?
>>>>
>>>> Config management code should not be mixed into the thing they are 
>>>> configuring,
>>>> this is just Good Programming. 
>>>
>>> I don't get it. Evidently I didn't take that course.
>>>
>>>> Changing variables on-the-fly is a completely
>>>> separate issue from config management and actually writing out those 
>>>> values,
>>>> and it was a mistake to bundle the two together in the code.
>>>
>>> The code that is "spread out over many files" has nothing to do with 
>>> writing the values out. All it does is changes the values. Sometimes it is 
>>> necessary to store a copy of the configured value so that it can be 
>>> returned exactly, most of the time it just gets and sets a variable.
>>>>
>>>> Using the guice framework would help this a lot as well.
>>>
>>> I am not convinced.
>>>>
>>>>>>
>>>>>> NB: I'm not implying you should be doing all the work toad, please don't 
>>>>>> think
>>>>>> this. And please don't think that my opinions are automatically invalid 
>>>>>> / less
>>>>>> worthy just because I haven't committed code for ages. I just want to 
>>>>>> express
>>>>>> my view of What I Would Do If I Were God.
>>>>>
>>>>> Okay. :) I don't mean to criticise un-constructively, just have a useful 
>>>>> conversation.
>>>>>>
>>>>>> People can do what they want but it doesn't mean it's a good idea. 
>>>>>> Granted, I
>>>>>> consider "code quality" independently of any issues such as funding, but
>>>>>> focusing too much on the latter leads to bad software. If there's 
>>>>>> pressure like
>>>>>> this, ignore it, find something else in the meantime until it goes away.
>>>>>
>>>>> I plan to continue working for FPI for the time being. Partly because 
>>>>> it's part time and fits conveniently with studying. Partly because 
>>>>> getting a programming job in the UK requires a degree even if you have 
>>>>> some rather odd experience.
>>>>>>
>>>>>> ## freenet Events API
>>>>>>
>>>>>> freenet does not provide a great API for running callbacks and scheduled 
>>>>>> tasks.
>>>>>
>>>>> I'm not sure what you mean here. For example, the client layer has unique 
>>>>> requirements. We simply can't just change it to use java.util.concurrent. 
>>>>> Database jobs must run on the database thread, and no other thread may 
>>>>> have access to the database, because the client layer is not designed to 
>>>>> deal with simultaneous transactions. This avoids performance problems 
>>>>> (separate caches for each client; simultaneous disk I/O), and complexity 
>>>>> (refreshing everything constantly, which requires a more query-oriented 
>>>>> architecture; we'd have to change pretty much everything, and it'd 
>>>>> probably be slower). But it's messy, in particular it requires passing 
>>>>> around ObjectContainer's.
>>>>
>>>> Why can't these restrictions be handled by a 3rd-party events framework?
>>>
>>> They can't. Period.
>>>
>>> The long answer is you can't pass an ObjectContainer to any other thread, 
>>> and any DBJob needs both the ObjectContainer and the ClientContext (which 
>>> isn't stored in any persistent class and is effectively the means to get 
>>> all the objects created for this run of the node that aren't persisted 
>>> otherwise, plus some global stuff like the FEC queue).
>>>
>>> The simplest way to enforce this rule is simply the ugly convention of 
>>> always passing the ObjectContainer in, and NOT either making it a singleton 
>>> global or storing it anywhere.
>>>
>>> The expensive way to deal with this would involve even more unwritten 
>>> rules: You could write a wrapper for ObjectContainer that throws if you try 
>>> to access it from another thread, for example.
>>>
>>> The really expensive way to deal with this is to have parallel 
>>> transactions. However, this way lies madness, because:
>>> 1. Typical end-user PCs' disks may be slower rather than faster with more 
>>> parallel transactions.
>>> 2. More importantly, there is *one cache per transaction*.
>>> 3. Code-wise, we would have to refresh everything every time we use it. 
>>> Although we already have a lot of activation and deactivation, so maybe 
>>> that's less of an issue. We would need to reconsider the "roots", and 
>>> probably make everything start with a query. Db4o queries generally are 
>>> rather slow so this would probably be either slow or messy (e.g. creating 
>>> key objects using strings etc, like p0s does in Freetalk and WoT).
>>> 4. We would also have to deal with rollbacks. Arguably these are a good 
>>> thing - p0s uses them - but that would mean that some easy optimisations 
>>> because extremely difficult. Once these issues are solved for Freetalk/WoT 
>>> (e.g. by means of a block level cache capable of caching more than one 
>>> transaction and then writing them to an alternate file, and alternating 
>>> between the two), it might be worth considering, but I doubt it would be 
>>> worth it.
>>>>
>>>>>> Converting everything to use java.util.concurrent and/or
>>>>>> com.google.common.util.concurrent would help this a lot. Of course, 
>>>>>> Library can
>>>>>> and currently does implement this itself, but it's fairly sloppy, and 
>>>>>> other
>>>>>> plugins would benefit if such a framework were provided.
>>>>>
>>>>> If you simply mean replacing Ticker and Executor, that's fine by me.
>>>>>>
>>>>>> Some common tasks that plugins would like to do, which really should be
>>>>>> provided by the underlying application:
>>>>>> - run tasks in the background
>>>>>> - run tasks according to a particular schedule
>>>>>> - cancel running tasks
>>>>>
>>>>> How do you propose to cancel a task once it's started? I guess it depends 
>>>>> what sort of task it is. If it has a boolean and periodically checks it 
>>>>> then fine; this would require a subclass ...
>>>>
>>>> Depends on what the task is. The writer of the "task" will need to add 
>>>> code to
>>>> support cancellation, just like Future.cancel().
>>>
>>> Okay.
>>>>
>>>>>> - handle dependencies between tasks, so that e.g. if A depends on B and I
>>>>>> cancel B, A is automatically cancelled.
>>>>>
>>>>> That's a nice bit of functionality yeah. Nothing in fred needs it at 
>>>>> present, although radical refactorings might make it more useful.
>>>>
>>>> Having this functionality would make Library's code a lot simpler. (The
>>>> dependency management that is, not necessarily the cancellation.) We talked
>>>> about it a while back but I never really sat down to solve the issue.
>>>
>>> I have no problem with importing a small third party library or even having 
>>> such classes in freenet/support/.
>>>>
>>>>>> - group tasks into related categories so one group doesn't affect the 
>>>>>> other.
>>>>>> (e.g. tasks for the group "Library/b-tree-write/index-<URL>" won't 
>>>>>> starve the
>>>>>> resources of group "WoT/background-trust-calculation")
>>>>>
>>>>> If you are scheduling tasks in a limited pool this makes sense. Freenet 
>>>>> generally doesn't do this because most tasks are either CPU intensive and 
>>>>> complete quickly, or are blocking / I/O intensive and take ages but don't 
>>>>> use much resources. Also many tasks are latency sensitive. And on modern 
>>>>> JVMs, lots of threads are cheap, although memory is an issue, we do need 
>>>>> to keep it down if possible ...
>>>>
>>>> There is a thread limit. You don't want plugin A to hog 90% threads and 
>>>> leave
>>>> the other 10% to everything else.
>>>
>>> It's different for plugins IMHO. Although there is only so much you can do 
>>> to guard against errant plugins.
>>>>
>>>>>>
>>>>>> ## config management
>>>>>>
>>>>>> Code for read/writing the config in embedded inside the classes they 
>>>>>> control,
>>>>>> which clutters up the code and makes it confusing. Config code should be
>>>>>> separated from the actual application. It would also be nice if this was
>>>>>> exposed to plugins.
>>>>>
>>>>> Separated how exactly? You want to call getters via reflection, like java 
>>>>> beans? It is always going to be the case that changing some settings on 
>>>>> the fly will be somewhat involved and require methods for it; the classes 
>>>>> are only one way to write such an interface, I'm open to other mechanisms 
>>>>> given that config happens very infrequently.
>>>>>>
>>>>>> Being a daemon is why the current config system is NOT SUFFICIENT. I 
>>>>>> need to be
>>>>>> able to lock certain config settings, such as where to keep the 
>>>>>> datastore /
>>>>>> run-time files themselves, to conform to the FHS. It's best that such 
>>>>>> settings
>>>>>> are kept in a separate read-only file. The current system only reads one 
>>>>>> file,
>>>>>> freenet.ini.
>>>>>
>>>>> Sounds to me like that could be achieved with some fairly small changes; 
>>>>> I still don't see what the problem is.
>>>>
>>>> Adding an "#include" mechanism for config files whilst supporting "write" 
>>>> at
>>>> the same time is not a "small change". Think about it.
>>>
>>> There are several cheap and dirty options, for example, we could provide a 
>>> list of config files on the command line, with changes being written to the 
>>> first (or the last, possibly configurable with another command line option).
>>>
>>> But yes, maybe we need something more elegant.
>>>
>>> But *we need to be able to write to a config file*. Ease of use on the 
>>> majority platform (Windows) makes this an absolute requirement. We cannot 
>>> get rid of it, period.
>>>>
>>>> (This is why .gitconfig doesn't have #include whereas .hgrc does.)
>>>>
>>>>>> Updating its own binaries is not a problem if freenet knows where the 
>>>>>> binaries
>>>>>> are. The current installer puts them in a rigid place, however this is
>>>>>> incompatible with FHS.
>>>>>
>>>>> A rigid place? I don't follow. If you are doing FHS you are using a 
>>>>> package; the installer is never going to comply with FHS as, apart from 
>>>>> anything else, it shouldn't be run as root!
>>>>
>>>> The updater (UpdateDeployContext) makes some very specific assumptions as 
>>>> to
>>>> the locations of the jars, and this method doesn't generalise to non-jar 
>>>> files.
>>>> It also assumes wrapper.conf is in the current directory.
>>>
>>> I believe we can ask the wrapper where the file is - and probably the jars 
>>> too.
>>>
>>> However I thought you were expecting to just turn off auto-update in 
>>> packaged versions?
>>>>
>>>>> Updating its own binaries is incompatible with the standard unix way of 
>>>>> doing things, isn't it? Even if it's not technically a violation of FHS?
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> Devl mailing list
>>>>> Devl at freenetproject.org
>>>>> https://emu.freenetproject.org/cgi-bin/mailman/listinfo/devl


-- 
GPG: 4096R/5FBBDBCE
https://github.com/infinity0
https://bitbucket.org/infinity0
https://launchpad.net/~infinity0

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 900 bytes
Desc: OpenPGP digital signature
URL: 
<https://emu.freenetproject.org/pipermail/devl/attachments/20120322/0110bf97/attachment.pgp>

Reply via email to